Predicción de Series de Tiempo

Contents

Predicción de Series de Tiempo#

Ejercicio 1.#

Considere la serie de tiempo asociada con los futuros de la criptomoneda Bitcoin desde que comenzó a comercializarse hasta la fecha del día de hoy. Utilice la API de Yahoo Finance para obtener esta serie de tiempo.

from datetime import datetime, timedelta
import datetime as dt 
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.graph_objects as go
import psycopg2
import pandas as pd 
import kaleido
import numpy as np
from scipy.stats import kstest
import statsmodels.api as sm
import scipy.stats as stats
import statsmodels.graphics.tsaplots as tsaplots
import yfinance as yf

data = yf.Ticker("BTC-USD")
data.info
{'name': 'Bitcoin',
 'startDate': 1278979200,
 'description': 'Bitcoin (BTC) is a cryptocurrency launched in 2010. Users are able to generate BTC through the process of mining. Bitcoin has a current supply of 19,685,875. The last known price of Bitcoin is 61,540.45487545 USD and is down -2.87 over the last 24 hours. It is currently trading on 11013 active market(s) with $42,618,670,077.96 traded over the last 24 hours. More information can be found at https://bitcoin.org/.',
 'maxAge': 86400,
 'priceHint': 2,
 'previousClose': 61281.86,
 'open': 61281.86,
 'dayLow': 60918.156,
 'dayHigh': 64120.5,
 'regularMarketPreviousClose': 61281.86,
 'regularMarketOpen': 61281.86,
 'regularMarketDayLow': 60918.156,
 'regularMarketDayHigh': 64120.5,
 'volume': 36111052800,
 'regularMarketVolume': 36111052800,
 'averageVolume': 35492800004,
 'averageVolume10days': 41660236821,
 'averageDailyVolume10Day': 41660236821,
 'marketCap': 1250881306624,
 'fiftyTwoWeekLow': 24797.168,
 'fiftyTwoWeekHigh': 73750.07,
 'fiftyDayAverage': 67298.07,
 'twoHundredDayAverage': 46786.8,
 'currency': 'USD',
 'fromCurrency': 'BTC',
 'toCurrency': 'USD=X',
 'lastMarket': 'CoinMarketCap',
 'coinMarketCapLink': 'https://coinmarketcap.com/currencies/bitcoin',
 'volume24Hr': 36111052800,
 'volumeAllCurrencies': 36111052800,
 'circulatingSupply': 19686462,
 'exchange': 'CCC',
 'quoteType': 'CRYPTOCURRENCY',
 'symbol': 'BTC-USD',
 'underlyingSymbol': 'BTC-USD',
 'shortName': 'Bitcoin USD',
 'longName': 'Bitcoin USD',
 'firstTradeDateEpochUtc': 1410912000,
 'timeZoneFullName': 'UTC',
 'timeZoneShortName': 'UTC',
 'uuid': '74397779-1589-3270-8c45-b7f1a7345b3a',
 'messageBoardId': 'finmb_BTC_CCC',
 'trailingPegRatio': None}
sns.set_theme() #para configurar el tema a usar en las figuras
sns.set_context("paper")
#Defición de parámetros para indexación de datos en BTC_df_sorted
stock = 'BTC-USD'
resolution = '1d'
#indexación de datos en BTC_df_sorted

BTC_df= yf.download("BTC-USD", interval=resolution)
BTC_df_sorted = BTC_df.sort_values(by='Date', ascending=True)
BTC_df_sorted.reset_index(inplace=True)
BTC_df_sorted.head()
[*********************100%%**********************]  1 of 1 completed

Date Open High Low Close Adj Close Volume
0 2014-09-17 465.864014 468.174011 452.421997 457.334015 457.334015 21056800
1 2014-09-18 456.859985 456.859985 413.104004 424.440002 424.440002 34483200
2 2014-09-19 424.102997 427.834991 384.532013 394.795990 394.795990 37919700
3 2014-09-20 394.673004 423.295990 389.882996 408.903992 408.903992 36863600
4 2014-09-21 408.084991 412.425995 393.181000 398.821014 398.821014 26580100

Detección de valores atípicos (outliers):

A continuación, se realiza un análisis para identificar la presencia de outliers en nuestro conjunto de datos. Para esto usamos el método de detección de valores atípicos mediante las puntuaciones \( Z \), el cual establece el siguiente criterio: Cualquier dato cuya puntuación esté fuera de la tercera desviación estándar es un valor atípico.

La función outliers_zscore recorrer todos los datos y calcular la puntuación \( Z \) para cada punto de datos utilizando la fórmula \( \frac{(x_i - \mu)}{\sigma} \), donde \( x_i \) es cada valor de los datos, \( \mu \) es la media de los datos y \( \sigma \) es la desviación estándar de los datos. Luego, establece un umbral en la tercera desviación estandar e identifica como valores atípicos aquellos datos cuya puntuación \( Z \) (en valor absoluto) excede este umbral.

from IPython.display import display, Markdown

display(Markdown('<center><img src="img/outlierszscore.png" alt="figure"></center>'))
figure
import numpy as np

#Definición de función para detección de datos atípicos a través de ZScore

def outliers_zscore(data):
    outliers = []    
    thres = 3
    mean = np.mean(data)
    std = np.std(data)
    for i in data:
        z_score = (i - mean)/std
        if (np.abs(z_score) > thres):
            outliers.append(i)
    return outliers

A continuación, se observan los datos atípicos obtenidos luego de aplicar el método descrito anteriormente.

BTC_df_outliers= BTC_df_sorted[["Close"]]
Close_outliers = outliers_zscore(BTC_df_outliers.Close)

outliers_df = pd.DataFrame({"Close_Outlieres": Close_outliers})

print("A continuación se imprimen Outliers a través Método Z-scores para el precio de Cierre del Bitcoin: ")
print(outliers_df)
A continuación se imprimen Outliers a través Método Z-scores para el precio de Cierre del Bitcoin: 
    Close_Outlieres
0      69019.789062
1      72123.906250
2      71481.289062
3      73083.500000
4      71396.593750
5      69403.773438
6      69958.812500
7      69987.835938
8      69455.343750
9      70744.953125
10     69892.828125
11     69645.304688
12     71333.648438
13     69702.148438
14     68896.109375
15     69362.554688
16     71631.359375
17     69139.015625
18     70587.882812
19     70060.609375

Basándonos en los resultados del Método Z-Scores, se confirma la presencia de valores atípicos en nuestro conjunto de datos. No obstante, optamos por no eliminarlos ni aplicar técnicas de imputación, ya que el número de valores atípicos no es significativo en relación con el tamaño total del conjunto de datos.

Gráfico de candlestick de datos de Bitcoin (BTC-USD).

import plotly.graph_objects as go
import plotly.io as pio
import plotly.offline as py

df_g = BTC_df_sorted.copy()

fig = go.Figure(data=[go.Candlestick(x = df_g['Date'],
                                     open = df_g['Open'], 
                                     high = df_g['High'],
                                     low = df_g['Low'], 
                                     close = df_g['Close'], 
                                     increasing_line_color='#008080',  # Color de línea de velas alcistas
                                     decreasing_line_color='#B22222',    # Color de línea de velas bajistas
                                     increasing_fillcolor='#B0E0E6',   # Color de relleno de velas alcistas
                                     decreasing_fillcolor='#DB7093'      # Color de relleno de velas bajistas)
                                     )
                        ]
                )      
                     
fig.update_layout(
    title="Bitcoin USD (BTC-USD)",
    xaxis_title="Day",
    yaxis_title="BTC-USD",
    font=dict(
        family="Verdana, monospace",
        size=12,
    )
)
fig.update_layout(xaxis_rangeslider_visible=False)
fig.show()

El gráfico de velas (candlestick) representa la evolución del precio de la criptomoneda Bitcoin desde el 17 de Septiembre de 2014 a la fecha, mostrando datos diarios. En este gráfico cada vela representa un día de trading:

  • Si el precio de apertura es mayor que el precio de cierre para ese día, la vela se dibuja de color azul, la cola superior de la vela representa el precio máximo alcanzado durante el día y la cola inferior de la vela representa el precio mínimo alcanzado durante el día.

  • Si el precio de cierre es mayor que el precio de apertura para ese día, la vela se dibuja de color rojo. De igual forma la cola superior de la vela representa el precio máximo alcanzado durante el día y la cola inferior de la vela representa el precio mínimo alcanzado durante el día.

En resumen, el gráfico de candlestick ofrece una representación visual de la variación diaria del precio de Bitcoin durante el periodo mencionado, mostrando tanto los rangos de precio máximo y mínimo como la relación entre el precio de apertura y el precio de cierre de cada día.

Serie de tiempo Bitcoin (BTC-UDS).

BTC_data = BTC_df_sorted.copy()

# Configurar estilo de seaborn y establecer el fondo en blanco
sns.set_style("whitegrid")

#Gráfico 
sns.lineplot(data=BTC_data, x='Date', y='Close', color='#008080')

# Personalizar etiquetas de los ejes y título del gráfico
plt.xlabel("Fecha", fontsize=10)  # Establecer etiqueta del eje x
plt.ylabel("Precio de Cierre (USD)", fontsize=10)  # Establecer etiqueta del eje y
plt.title("Precio de cierre de Bitcoin a lo largo del tiempo", fontsize=12)  # Establecer título del gráfico
Text(0.5, 1.0, 'Precio de cierre de Bitcoin a lo largo del tiempo')
_images/20e3f4c90ce69a5179743f3c4eebae64fec01e1d0bca195bdcefb7d9b14cfa03.png

El gráfico ilustra la evolución del precio de Bitcoin desde el 17 de septiembre de 2014 hasta la fecha actual. Durante este período, el precio se mantuvo relativamente estable hasta 2017, momento en el que experimentó un notable aumento. En 2021, hubo un nuevo y significativo incremento, seguido de un declive en 2022. Sin embargo, en 2023 se observa una recuperación y el precio continúa mostrando una tendencia alcista. Dado este comportamiento ascendente a lo largo del tiempo, podemos concluir que el gráfico representa la evolución del precio de Bitcoin. Además, la serie de tiempo correspondiente se caracteriza como no estacionaria.

Una serie de tiempo se considera No estacionaria si la distribución de sus datos (media, varianza, autocorrelación, etc.) no permanece constante a lo largo del tiempo, es decir, si muestra tendencia, variación en la varianza, autocorrelación significativa.

Ejercicio 2.#

Repita TODOS los pasos indicados en esta sección para encontrar modelos ARIMA para predecir el precio de Bitcoin con los siguientes horizontes: 7, 14, 21 y 28 días. Utilizar siempre predicciones usando rolling con ventana de predicción continua de un día. Cualquier cantidad de pasos extra para enriquecer su análisis predictivo serán aceptados siempre y cuando sean acordes con lo que indica la teoría de análisis de series de tiempo.

from IPython.display import display, Markdown

display(Markdown('<center><img src="img/ejercicio2.png" alt="figure"></center>'))
figure

¿La serie de tiempo es estacionaria o no es estacionaria?#

Primeramente, verificaremos si la serie de tiempo es NO estacionaria. Para esto usamos la prueba de Dickey-Fuller, ya que, para aplicar un modelo ARIMA nuestra serie de tiempo debe ser estacionaria. Para esto, procedemos con la instalación de la libreria statemodels a través del siguiente comando:

  • pip install statsmodels.

from statsmodels.tsa.stattools import adfuller
from numpy import log
  • Planteamiento de Hipótesis:

\[ H_0 : \text{La serie de tiempo es no estacionaria.}\]
\[ H_1 : \text{La serie de tiempo es estacionaria.}\]
  • Críterios de aceptación y/o rechazo:

Sí el P-value \(< 0.05 \), se rechaza la hipótesis nula con un a significancia \( \alpha = 0.05\), es decir que la serie de tiempo es estacionaria.

result = adfuller(BTC_df_sorted.Close)
print('ADF Statistic: %f' % result[0])
print('p-value: %f' % result[1])
ADF Statistic: -0.812080
p-value: 0.815548

De acuerdo al resultado obtenido con la prueba de Dickey-Fuller con P-value \(= 0,8189 > 0,05\), por tanto se rechaza la hipótesis alternativa con una significancia \(\alpha = 0.05\), es decir que la serie de tiempo es NO estacionaria.

A continuación gráficamos la Autocorrelación y estacionalidad de la serie de tiempo asociada con los futuros de la criptomoneda Bitcoin (BTC-USD).

import matplotlib.pyplot as plt
from statsmodels.graphics.tsaplots import plot_acf 
from statsmodels.tsa.stattools import acf
import pandas as pd
fig, axes = plt.subplots(nrows=1, ncols=2, sharex=True,figsize=(18,6))

axes[0].plot(BTC_df_sorted.Close, color ='#008080') 
axes[0].set_title('Original Series', fontsize=16)
axes[0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)

figtest0=plot_acf(BTC_df_sorted.Close, ax=axes[1], lags = 240, color = '#008080', vlines_kwargs ={"colors":'#008080'})
axes[1].set_title('Autocorrelation', fontsize=16)
axes[1].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[1].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
_images/e27ad089726d1fd4fb29ec4d1c864300ad880acea60d85cd406b8dc9cb76b9a5.png
  • Gráfico Original Series: El gráfico representa el comportamiento del precio de la criptomoneda Bitcoin en un periodo de tiempo. Al tener un comportamiento acendente en el tiempo, se puede afirmar que representa la evolución del precio del Bitcoin a lo largo del tiempo.

  • Gráfico Autocorrelation Original Series: Como se puede observar en gráfico ACF, la autocorrelación de la serie con sus rezagos decae lentamente en el tiempo de forma lineal; ésto es coherente con el resultado obtenido luego de aplicar la prueba de Dickey-Fuller.

Eliminación de la tendencia entre la serie y sus rezagos.#

La eliminación de la tendencia y de la correlación entre la serie y sus rezagos puede hacerse por diferenciación.

Diferenciación de 1er Orden:#

fig, axes = plt.subplots(nrows=1, ncols=2, sharex=True,figsize=(18,6))

axes[0].plot(BTC_df_sorted.Close.diff(), color ='#008080'); 
axes[0].set_title('1st Order Differencing', fontsize=16)
axes[0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)

figtest1=plot_acf(BTC_df_sorted.Close.diff().dropna(), ax=axes[1], lags = 240, color = '#008080', vlines_kwargs ={"colors":'#008080'})
axes[1].set_title('Autocorrelation', fontsize=16)
axes[1].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[1].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
_images/07f1580eb50b7fb2a88712c0c2bf8d0e1c2fbadaff8618788811338333a3469d.png

Luego de aplicar una diferenciación de 1er orden, procedemos aplicar nuevamente la prueba de Dickey-Fuller para validar si nuestra serie de tiempo es estacionaria.

from statsmodels.tsa.stattools import adfuller
from numpy import log

result1 = adfuller(BTC_df_sorted.Close.diff().dropna())
print('ADF Statistic: %f' % result1[0])
print('p-value: %f' % result1[1])
ADF Statistic: -8.800824
p-value: 0.000000

De acuerdo al resultado obtenido con la prueba de Dickey-Fuller con P-value \(<0.000000\), por tanto no se rechaza la hipótesis nula con una significancia \(\alpha = 0.05\), es decir que la serie de tiempo es estacionaria.

Diferenciación de 2do Orden:#

fig, axes = plt.subplots(nrows=1, ncols=2, sharex=True,figsize=(18,6))

axes[0].plot(BTC_df_sorted.Close.diff().diff(), color ='#008080')
axes[0].set_title('2do Order Differencing',fontsize=16)
axes[0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)

figtest2=plot_acf(BTC_df_sorted.Close.diff().diff().dropna(), ax=axes[1], lags = 240, color = '#008080', vlines_kwargs ={"colors":'#008080'})
axes[1].set_title('Autocorrelation', fontsize=16)
axes[1].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[1].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
_images/c92fc464777d964bd62ea47353420dca8cff835cbc6e419ddd343cbf87573059.png

Luego de aplicar una diferenciación de 2do orden, procedemos aplicar nuevamente la prueba de Dickey-Fuller para validar si nuestra serie de tiempo continua siendo estacionaria.

from statsmodels.tsa.stattools import adfuller
from numpy import log

result2 = adfuller(BTC_df_sorted.Close.diff().diff().dropna())
print('ADF Statistic: %f' % result2[0])
print('p-value: %f' % result2[1])
ADF Statistic: -19.899003
p-value: 0.000000

De acuerdo al resultado obtenido con la prueba de Dickey-Fuller con P-value \(<0.00000\), por tanto no se rechaza la hipótesis nula con una significancia \(\alpha = 0.05\), es decir que la serie de tiempo es no estacionaria. Sin embargo al momento de análizar el gráfico de autocorrelación para la 2da diferenciación, el retraso entra en el zona negativa muy distante de nuestro limite de tolerancia, lo que indica que la serie podría haber sido sobrediferenciada.

Por lo tanto, vamos a fijar el orden de diferenciación como 1, es decir, \( d = 1\) .

Construcción de modelo ARIMA para predecir el precio de Bitcoin con los siguientes horizontes: 7, 14, 21 y 28#

Función de Ajuste del modelo y predicciones ARIMA#

from statsmodels.tsa.arima.model import ARIMA
import warnings
warnings.filterwarnings("ignore") # suprimir advertencias durante el ajuste del modelo.


def arima_model_fit(data_df):
    # Realiza predicciones continuas usando un enfoque de desplazamiento
    # y almacena las predicciones en una lista
    data = data_df.copy()

    n_BTC = len(data.Close)
    train_size = n_BTC - 28  # Tamaño del conjunto de entrenamiento - 28 días

    train = data.Close[:train_size]  # Datos de entrenamiento (precios de cierre)
    dates_train = data.Date[:train_size]  # Fechas correspondientes a los datos de entrenamiento

#A través de la siguiente función que depende sólo del input `train` y retornarán los parámetros `p`, `d`, `q` y que definiendo el mejor modelo ARIMA con el menor criterio `AIC` de bondad de ajuste.
#**Nota:** Consideramos el `método: mle`para el cálculo de verosimilitud a través del filtro de Kalman. 

    best_aic = np.inf
    best_bic = np.inf

    best_order = None
    best_mdl = None

    pq_rng = range(3)
    d_rng  = range(2)

    for p in pq_rng:
        for d in d_rng:
            for q in pq_rng:
                try:
                    # print(i, d, j)
                    tmp_mdl = ARIMA(train, order=(p,d,q)).fit()
                    tmp_aic = tmp_mdl.aic
                    if tmp_aic < best_aic:
                        best_aic = tmp_aic
                        best_order = (p, d, q)
                        best_mdl = tmp_mdl
                except: continue

    print('Best AIC:', best_aic)
    print('Best Order:', best_order)

    print('aic: {:6.5f} | order: {}'.format(best_aic, best_order))

    model = ARIMA(train, order=best_order)
    model_fit = model.fit()   

    return model_fit, train, dates_train, best_order

Función Rolling forecast#

def arima_rolling(history, test, best_order): # arima_rolling(train.tolist(), test_nd, best_order)
    
    predictions = list()
    for t in range(len(test)):
        model = ARIMA(history, order=best_order)
        model_fit = model.fit()
        output = model_fit.forecast()
        yhat = output[0]
        predictions.append(yhat)
        obs = test[t]
        history.append(obs)
        print('predicted=%f, expected=%f' % (yhat, obs))
        
    return predictions

Indexación de parámetros para la función arima_model_fit()#

La siguiente función devuelve los parámetros p, d, q que definen el mejor modelo ARIMA con el menor criterio AIC (Criterio de Información de Akaike) de bondad de ajuste. Además, proporciona los resultados del modelo ARIMA ajustado para datos de entrenamiento, incluyendo las fechas correspondientes. También incluye los datos y fechas del modelo ARIMA de prueba, aunque estos últimos dos no son directamente visibles en la salida de la función.

model_fit,train,dates_train,best_order = arima_model_fit(BTC_df_sorted)
Best AIC: 56472.04159778856
Best Order: (2, 1, 2)
aic: 56472.04160 | order: (2, 1, 2)

Según el criterio de AIC, se determinó que el modelo ARIMA óptimo es (2,1,2).

Nota: La función auto_arima de Python es útil en ciertos casos específicos. Sin embargo, este no es uno de ellos, ya que el modelo ARIMA que proporciona es simplemente un random walk. Este tipo de modelo predice puramente como un modelo estocástico con dependencia temporal basada únicamente en el punto temporal anterior. Los datos financieros, especialmente los precios de activos como el Bitcoin, suelen ser muy volátiles y pueden mostrar comportamientos no lineales o estacionarios cambiantes en el tiempo. Esto puede dificultar que un modelo auto_arima capture la complejidad de los datos y construya el mejor modelo. Por esta razón, no utilizaremos esta función.

Indexación de parámetros para la función arima_rolling()#

La siguiente función devuelve las prediciones, con base a los parámetros p, d, q que definen el mejor modelo ARIMA con el menor criterio AIC (Criterio de Información de Akaike) de bondad de ajuste, aprovechando datos históricos para pronosticar continuamente el precio futuro del Bitcoin durante un período (horizonte) específico.

  • Horizonte de 7 días

train_size = len(BTC_df_sorted.Close) - 28
test_7 = BTC_df_sorted.Close[train_size:train_size + 7]
dates_7 = BTC_df_sorted.Date[train_size:train_size + 7]

test_7l = test_7.tolist()
train_7l = train.tolist()
yhat_7  = arima_rolling(train_7l, test_7l, best_order)
predicted=65458.494464, expected=63778.761719
predicted=63860.747030, expected=64062.203125
predicted=64096.574807, expected=67234.171875
predicted=67062.720436, expected=69958.812500
predicted=69768.273352, expected=69987.835938
predicted=70002.110586, expected=69455.343750
predicted=69664.323396, expected=70744.953125
  • Horizonte de 14 días

train_size = len(BTC_df_sorted.Close) - 28
test_14 = BTC_df_sorted.Close[train_size:train_size + 14]
dates_14 = BTC_df_sorted.Date[train_size:train_size + 14]

test_14l = test_14.tolist()
train_14l = train.tolist()
yhat_14  = arima_rolling(train_14l, test_14l, best_order)
predicted=65458.494464, expected=63778.761719
predicted=63860.747030, expected=64062.203125
predicted=64096.574807, expected=67234.171875
predicted=67062.720436, expected=69958.812500
predicted=69768.273352, expected=69987.835938
predicted=70002.110586, expected=69455.343750
predicted=69664.323396, expected=70744.953125
predicted=70861.279487, expected=69892.828125
predicted=69812.695365, expected=69645.304688
predicted=69475.677678, expected=71333.648438
predicted=71183.721412, expected=69702.148438
predicted=69815.816683, expected=65446.972656
predicted=65828.220564, expected=65980.812500
predicted=66086.075423, expected=68508.843750
  • Horizonte de 21 días

train_size = len(BTC_df_sorted.Close) - 28
test_21 = BTC_df_sorted.Close[train_size:train_size + 21]
dates_21 = BTC_df_sorted.Date[train_size:train_size + 21]

test_21l = test_21.tolist()
train_21l = train.tolist()
yhat_21  = arima_rolling(train_21l, test_21l, best_order)
predicted=65458.494464, expected=63778.761719
predicted=63860.747030, expected=64062.203125
predicted=64096.574807, expected=67234.171875
predicted=67062.720436, expected=69958.812500
predicted=69768.273352, expected=69987.835938
predicted=70002.110586, expected=69455.343750
predicted=69664.323396, expected=70744.953125
predicted=70861.279487, expected=69892.828125
predicted=69812.695365, expected=69645.304688
predicted=69475.677678, expected=71333.648438
predicted=71183.721412, expected=69702.148438
predicted=69815.816683, expected=65446.972656
predicted=65828.220564, expected=65980.812500
predicted=66086.075423, expected=68508.843750
predicted=68210.458405, expected=67837.640625
predicted=67719.156050, expected=68896.109375
predicted=68881.661974, expected=69362.554688
predicted=69468.841935, expected=71631.359375
predicted=71671.930041, expected=69139.015625
predicted=69355.259998, expected=70587.882812
predicted=70566.796147, expected=70060.609375
  • Horizonte de 28 días

train_size = len(BTC_df_sorted.Close) - 28
test_28 = BTC_df_sorted.Close[train_size:train_size + 28]
dates_28 = BTC_df_sorted.Date[train_size:train_size + 28]

test_28l = test_28.tolist()
train_28l = train.tolist()
yhat_28  = arima_rolling(train_28l, test_28l, best_order)
predicted=65458.494464, expected=63778.761719
predicted=63860.747030, expected=64062.203125
predicted=64096.574807, expected=67234.171875
predicted=67062.720436, expected=69958.812500
predicted=69768.273352, expected=69987.835938
predicted=70002.110586, expected=69455.343750
predicted=69664.323396, expected=70744.953125
predicted=70861.279487, expected=69892.828125
predicted=69812.695365, expected=69645.304688
predicted=69475.677678, expected=71333.648438
predicted=71183.721412, expected=69702.148438
predicted=69815.816683, expected=65446.972656
predicted=65828.220564, expected=65980.812500
predicted=66086.075423, expected=68508.843750
predicted=68210.458405, expected=67837.640625
predicted=67719.156050, expected=68896.109375

Gráfica lineplot modelo ARIMA_rolling para predecir el precio de Bitcoin con un horizonte de 7, 14, 21 y 28 días#

import matplotlib.pyplot as plt
import seaborn as sns


fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(25, 25)) # Crear la figura y el eje de la subtrama

#Gráfica horizonte 7 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[0], color='#009900', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_7, y=test_7, ax=axes[0], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=yhat_7, ax=axes[0], label='Forecast', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[0].set_xlabel(None)
axes[0].legend(fontsize=14) # Mostrar la leyenda
axes[0].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[0].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 14 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[1], color='#009900', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_14, y=test_14, ax=axes[1], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=yhat_14, ax=axes[1], label='Forecast', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[1].set_xlabel(None)
axes[1].legend(fontsize=14) # Mostrar la leyenda
axes[1].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[1].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 21 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[2], color='#009900', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_21, y=test_21, ax=axes[2], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=yhat_21, ax=axes[2], label='Forecast', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[2].set_xlabel(None)
axes[2].legend(fontsize=14) # Mostrar la leyenda
axes[2].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[2].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 28 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[3], color='#009900', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_28, y=test_28, ax=axes[3], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=yhat_28, ax=axes[3], label='Forecast', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[3].set_xlabel('Fecha', fontsize=12)
axes[3].legend(fontsize=14) # Mostrar la leyenda
axes[3].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[3].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

plt.show()
_images/422c8034598ce753bb92c3b7477d79ed84f13a1286d100a008094273f148e21e.png

En el gráfico anterior se presentan las predicciones para horizontes de 7, 14, 21 y 28 días. La línea verde representa los datos utilizados para entrenar el modelo ARIMA. La línea azul muestra los datos reales u observaciones correspondientes a cada horizonte, mientras que la línea punteada indica las predicciones generadas por nuestro modelo ARIMA a partir de los datos de entrenamiento. Estas predicciones se comparan con los valores reales de prueba para evaluar la precisión del modelo.

También se aprecia un sesgo entre los datos reales de prueba y las predicciones proporcionadas por nuestro modelo ARIMA, basadas en los datos de entrenamiento.

Ejercicio 3.#

Repita el paso 2 ahora sin utilizar rolling. Esto es, realice el pronóstico solo utilizando forecast() para los diferentes horizontes de predicción, 7, 14, 21 y 28 días.

A continuación se ajusta nuestro modelo ARIMA utilizando forecast() para los diferentes horizontes de predicción, 7, 14, 21 y 28 días, y así poder visualizar las predicciones del precio de cierre de la criptomoneda Bitcoin.

  • Horizonte de 7 días

predic_forecast7 = model_fit.forecast(7) 
print(predic_forecast7)
3474    65458.494464
3475    65469.217327
3476    65506.959254
3477    65528.716848
3478    65513.547322
3479    65481.913168
3480    65469.104822
Name: predicted_mean, dtype: float64
  • Horizonte de 14 días

predic_forecast14 = model_fit.forecast(14) 
print(predic_forecast14)
3474    65458.494464
3475    65469.217327
3476    65506.959254
3477    65528.716848
3478    65513.547322
3479    65481.913168
3480    65469.104822
3481    65486.302922
3482    65511.754461
3483    65517.675011
3484    65500.216718
3485    65480.598282
3486    65479.724074
3487    65496.228503
Name: predicted_mean, dtype: float64
  • Horizonte de 21 días

predic_forecast21 = model_fit.forecast(21) 
print(predic_forecast21)
3474    65458.494464
3475    65469.217327
3476    65506.959254
3477    65528.716848
3478    65513.547322
3479    65481.913168
3480    65469.104822
3481    65486.302922
3482    65511.754461
3483    65517.675011
3484    65500.216718
3485    65480.598282
3486    65479.724074
3487    65496.228503
3488    65510.628019
3489    65508.029204
3490    65493.238830
3491    65483.304624
3492    65488.086505
3493    65500.759109
3494    65507.027237
Name: predicted_mean, dtype: float64
  • Horizonte de 28 días

predic_forecast28 = model_fit.forecast(28) 
print(predic_forecast28)
3474    65458.494464
3475    65469.217327
3476    65506.959254
3477    65528.716848
3478    65513.547322
3479    65481.913168
3480    65469.104822
3481    65486.302922
3482    65511.754461
3483    65517.675011
3484    65500.216718
3485    65480.598282
3486    65479.724074
3487    65496.228503
3488    65510.628019
3489    65508.029204
3490    65493.238830
3491    65483.304624
3492    65488.086505
3493    65500.759109
3494    65507.027237
3495    65501.076908
3496    65490.658316
3497    65487.277885
3498    65493.634124
3499    65501.852359
3500    65503.058979
3501    65496.839353
Name: predicted_mean, dtype: float64

Gráfica lineplot modelo ARIMA para predecir el precio de Bitcoin con un horizonte de 7, 14, 21 y 28 días#

import matplotlib.pyplot as plt
import seaborn as sns


fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(25, 25)) # Crear la figura y el eje de la subtrama

#Gráfica horizonte 7 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[0], color='#009900', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_7, y=test_7, ax=axes[0], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=predic_forecast7, ax=axes[0], label='Forecast', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[0].set_xlabel(None)
axes[0].legend(fontsize=14) # Mostrar la leyenda
axes[0].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[0].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 14 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[1], color='#009900', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_14, y=test_14, ax=axes[1], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=predic_forecast14, ax=axes[1], label='Forecast', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[1].set_xlabel(None)
axes[1].legend(fontsize=14) # Mostrar la leyenda
axes[1].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[1].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 21 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[2], color='#009900', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_21, y=test_21, ax=axes[2], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=predic_forecast21, ax=axes[2], label='Forecast', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[2].set_xlabel(None)
axes[2].legend(fontsize=14) # Mostrar la leyenda
axes[2].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[2].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 28 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[3], color='#009900', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_28, y=test_28, ax=axes[3], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=predic_forecast28, ax=axes[3], label='Forecast', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[3].set_xlabel('Fecha', fontsize=12)
axes[3].legend(fontsize=14) # Mostrar la leyenda
axes[3].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[3].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

plt.show()
_images/5ab0b92a6061a8e357711eea6d41e0dd50d36303506b568fbb8e9a0f6639babb.png

El gráfico previo representa la predicción del modelo ARIMA sin utilizar el método rolling, con un horizonte de pronóstico de un período. En el contexto de nuestros modelos ARIMA, este período se define por los días definidos en cada uno de nuestros horizontes. El gráfico muestra la predicción para 7, 14, 21 y 28 días que le anteceden al último dato disponible en el conjunto de datos original. Además, se observan diferencias significativas entre las observaciones de prueba y las predicciones realizadas para cada uno de los horizontes.

Ejercicio 4.#

Realice tablas de error para los ejercicios 1 y 2, utilizando las métricas: MAPE, MAE, RMSE, MSE, R2. Además, agregue el gráfico de correlación entre la observación real y su predicción en el test, \( Corr(y_t, \tilde{y}_t) \).

Tablas de error para los ejercicios 2 (usando rolling) y 3 (sin utilizar rolling), utilizando las métricas: MAPE, MAE, RMSE, MSE, R2.#

Para medir el error de predicción cometido en las predicciones, utilizaremos las métricas usuales en análisis de series de tiempo: MAPE, MAE, RMSE, MSE, R2, la cuales se calcularán a través de la siguiente función:

from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

def forecast_accuracy(forecast, actual, str_name):
    # Calcula diferentes métricas de evaluación (MAPE, MAE, RMSE, MSE, R2)
    # y las almacena en un DataFrame
    
    mape = np.mean(np.abs(forecast - actual) / np.abs(actual)) # Calcular el Error Porcentual Absoluto Medio (MAPE)     
    r2 = r2_score(actual, forecast) # Calcular el Coeficiente de Determinación (R2)
    rmse  = mean_squared_error(actual, forecast, squared=False) # Calcular la Raíz del Error Cuadrático Medio (RMSE)
    mse  = mean_squared_error(actual, forecast, squared=True) # Calcular el Error Cuadrático Medio (MSE)
    mae = mean_absolute_error(actual, forecast) # Calcular el Error Absoluto Medio (MAE)


    # Crear un DataFrame con las métricas calculadas
    df_acc = pd.DataFrame({
        'MAPE': [mape],
        'MAE': [mae],
        'RMSE': [rmse],
        'MSE': [mse],               
        'R2': [r2]
    }, index=[str_name])  # Usar el nombre proporcionado como índice
    
    return df_acc

Evaluación de las predicciones ejercicio 2 (usando rolling).#

  • Horizonte de 7 días

fraic7 = forecast_accuracy(np.array(yhat_7), np.array(test_7l), "AIC usando Rolling 7 días")
fraic7 = fraic7.round(3)
print(fraic7)
                            MAPE       MAE      RMSE          MSE     R2
AIC usando Rolling 7 días  0.021  1394.548  1797.175  3229839.082  0.559
  • Horizonte de 14 días

fraic14 =forecast_accuracy(np.array(yhat_14), np.array(test_14l), "AIC usando Rolling 14 días")
fraic14 = fraic14.round(3)
print(fraic14)
                             MAPE       MAE      RMSE          MSE     R2
AIC usando Rolling 14 días  0.022  1512.959  1967.627  3871556.705  0.335
  • Horizonte de 21 días

fraic21 = forecast_accuracy(np.array(yhat_21), np.array(test_21l), "AIC usando Rolling 21 días")
fraic21 = fraic21.round(3)
print(fraic21)
                             MAPE      MAE      RMSE          MSE     R2
AIC usando Rolling 21 días  0.021  1411.73  1810.354  3277380.241  0.309
  • Horizonte de 28 días

fraic28= forecast_accuracy(np.array(yhat_28), np.array(test_28l), "AIC usando Rolling 28 días")
fraic28r = fraic28.round(3)
print(fraic28r)
                             MAPE       MAE      RMSE         MSE     R2
AIC usando Rolling 28 días  0.024  1599.699  1957.321  3831106.52  0.537

Al observar estas métricas, podemos afirmar que el modelo ARIMA tiene un mejor desempeño en la predicción de series temporales para un horizonte de 7 días, mostrando una mayor precisión y capacidad para explicar la variabilidad en los datos en comparación con los horizontes de tiempo más largos.

Evaluación de las predicciones ejercicio 3 (Sin utilizar rolling).#

  • Horizonte de 7 días

faic7 = forecast_accuracy(np.array(predic_forecast7), np.array(test_7l), "AIC sin Rolling7 días")
faic7 = faic7.round(3)
print(faic7)
                        MAPE       MAE      RMSE           MSE     R2
AIC sin Rolling7 días  0.048  3281.089  3606.585  1.300746e+07 -0.776
  • Horizonte de 14 días

faic14 = forecast_accuracy(np.array(predic_forecast14), np.array(test_14l), "AIC sin Rolling 14 días")
faic14 = faic14.round(3)
print(faic14)
                          MAPE       MAE      RMSE           MSE     R2
AIC sin Rolling 14 días  0.046  3219.495  3668.064  1.345470e+07 -1.311
  • Horizonte de 21 días

faic21= forecast_accuracy(np.array(predic_forecast21), np.array(test_21l), "AIC sin Rolling 21 días")
faic21 = faic21.round(3)
print(faic21)
                          MAPE      MAE      RMSE           MSE     R2
AIC sin Rolling 21 días  0.051  3528.43  3891.219  1.514158e+07 -2.194
  • Horizonte de 28 días

faic28 = forecast_accuracy(np.array(predic_forecast28), np.array(test_28l), "AIC sin Rolling 28 días")
faic28 = faic28.round(3)
print(faic28)
                          MAPE       MAE      RMSE           MSE     R2
AIC sin Rolling 28 días  0.046  3126.999  3546.356  1.257664e+07 -0.521

Los resultados anteriores indican que el modelo ARIMA sin el método de Rolling tiene un desempeño notablemente inferior en la predicción de series temporales en contraste con el modelo usando Rolling. Las métricas de evaluación señalan una discrepancia más amplia entre las predicciones del modelo y los valores reales, junto con una capacidad restringida para explicar la variabilidad en los datos.

A continuación se presenta análisis de correlación entre variables (observación real y su predicción en el test, \( Corr(y_t, \tilde{y}_t) \).) para evaluar la relación lineal entre éstas a través de mapa de calor..

Gráfico de correlación entre la observación real y su predicción en el test, \( Corr(y_t, \tilde{y}_t) \).#

Gráfico de Correlación ejercicio 2 (usando rolling). .#

import matplotlib.pyplot as plt
import seaborn as sns

fig, axes = plt.subplots(nrows=4, ncols=2, figsize=(25, 25))  # Crear la figura y el eje de la subtrama

# Gráfica horizonte 7 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_7, y=test_7, ax=axes[0,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=yhat_7, ax=axes[0,0], label='Predicción', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0,0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[0,0].set_xlabel(None)
axes[0,0].legend(fontsize=10) # Mostrar la leyenda
axes[0,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[0,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_7l, y=yhat_7, ax=axes[0,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[0,1].plot(test_7l, test_7l, color='red', label='Correlación')
axes[0,1].set_title('Correlación entre valores reales y estimados Horizonte 7 días', fontsize=16)
axes[0,1].set_xlabel('Valores Reales', fontsize=12)
axes[0,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[0,1].legend(fontsize=10)  # Mostrar la leyenda
axes[0,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[0,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)

# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_14, y=test_14, ax=axes[1,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=yhat_14, ax=axes[1,0], label='Predicción', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1,0].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[1,0].set_xlabel(None)
axes[1,0].legend(fontsize=10) # Mostrar la leyenda
axes[1,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[1,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_14l, y=yhat_14, ax=axes[1,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[1,1].plot(test_14l, test_14l, color='red', label='Correlación')
axes[1,1].set_title('Correlación entre valores reales y estimados Horizonte 14 días ', fontsize=16)
axes[1,1].set_xlabel('Valores Reales', fontsize=12)
axes[1,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[1,1].legend(fontsize=10)  # Mostrar la leyenda
axes[1,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[1,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_21, y=test_21, ax=axes[2,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=yhat_21, ax=axes[2,0], label='Predicción', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2,0].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[2,0].set_xlabel(None)
#axes[2,0].legend(fontsize=10) # Mostrar la leyenda
axes[2,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[2,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_21l, y=yhat_21, ax=axes[2,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[2,1].plot(test_21l, test_21l, color='red', label='Correlación')
axes[2,1].set_title('Correlación entre valores reales y estimados Horizonte 21 días ', fontsize=16)
axes[2,1].set_xlabel('Valores Reales', fontsize=12)
axes[2,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[2,1].legend(fontsize=10)  # Mostrar la leyenda
axes[2,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[2,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_28, y=test_28, ax=axes[3,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=yhat_28, ax=axes[3,0], label='Predicción', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3,0].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[3,0].set_xlabel(None)
#axes[3,0].legend(fontsize=10) # Mostrar la leyenda
axes[3,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[3,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_28l, y=yhat_28, ax=axes[3,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[3,1].plot(test_28l, test_28l, color='red', label='Correlación')
axes[3,1].set_title('Correlación entre valores reales y estimados Horizonte 28 días ', fontsize=16)
axes[3,1].set_xlabel('Valores Reales', fontsize=12)
axes[3,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[3,1].legend(fontsize=10)  # Mostrar la leyenda
axes[3,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[3,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)



plt.tight_layout()  # Ajustar el espaciado entre subgráficos
plt.show()
_images/f940bfd1292f8bf742cf2e02a46307fe864f37684f6ddd77fb2b9b26ac49fd22.png

El gráfico muestra predicciones para diferentes horizontes temporales (7, 14, 21 y 28 días) junto con observaciones reales de prueba. También se incluye un gráfico que correlaciona las predicciones con estas observaciones reales. En relación al primer gráfico del precio de cierre del Bitcoin, se aprecia que ambas líneas siguen tendencias similares y están próximas entre sí, lo que indica errores bajos. Sin embargo, se observa un sesgo en el tiempo que podría corregirse mediante mejoras en el modelo o la exploración de otras opciones que expliquen con mayor precisión la variabilidad en el tiempo. Los gráficos de autocorrelación revelan la ausencia de una correlación significativa entre los valores de la serie temporal y sus antecesores en los intervalos de 7, 14, 21 y 28 días. En resumen, no se observa un patrón claro o repetitivo en los datos a lo largo de estos horizontes.

Gráfico de Correlación ejercicio 3 (Sin utilizar rolling).#

import matplotlib.pyplot as plt
import seaborn as sns

fig, axes = plt.subplots(nrows=4, ncols=2, figsize=(25, 25))  # Crear la figura y el eje de la subtrama

# Gráfica horizonte 7 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_7, y=test_7, ax=axes[0,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=predic_forecast7, ax=axes[0,0], label='Predicción', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0,0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[0,0].set_xlabel(None)
axes[0,0].legend(fontsize=10) # Mostrar la leyenda
axes[0,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[0,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_7l, y=predic_forecast7, ax=axes[0,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[0,1].plot(test_7l, test_7l, color='red', label='Correlación')
axes[0,1].set_title('Correlación entre valores reales y estimados Horizonte 7 días', fontsize=16)
axes[0,1].set_xlabel('Valores Reales', fontsize=12)
axes[0,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[0,1].legend(fontsize=10)  # Mostrar la leyenda
axes[0,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[0,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)

# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_14, y=test_14, ax=axes[1,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=predic_forecast14, ax=axes[1,0], label='Predicción', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1,0].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[1,0].set_xlabel(None)
axes[1,0].legend(fontsize=10) # Mostrar la leyenda
axes[1,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[1,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_14l, y=predic_forecast14, ax=axes[1,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[1,1].plot(test_14l, test_14l, color='red', label='Correlación')
axes[1,1].set_title('Correlación entre valores reales y estimados Horizonte 14 días ', fontsize=16)
axes[1,1].set_xlabel('Valores Reales', fontsize=12)
axes[1,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[1,1].legend(fontsize=10)  # Mostrar la leyenda
axes[1,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[1,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_21, y=test_21, ax=axes[2,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=predic_forecast21, ax=axes[2,0], label='Predicción', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2,0].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[2,0].set_xlabel(None)
#axes[2,0].legend(fontsize=10) # Mostrar la leyenda
axes[2,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[2,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_21l, y=predic_forecast21, ax=axes[2,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[2,1].plot(test_21l, test_21l, color='red', label='Correlación')
axes[2,1].set_title('Correlación entre valores reales y estimados Horizonte 21 días ', fontsize=16)
axes[2,1].set_xlabel('Valores Reales', fontsize=12)
axes[2,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[2,1].legend(fontsize=10)  # Mostrar la leyenda
axes[2,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[2,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_28, y=test_28, ax=axes[3,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=predic_forecast28, ax=axes[3,0], label='Predicción', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3,0].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[3,0].set_xlabel(None)
#axes[3,0].legend(fontsize=10) # Mostrar la leyenda
axes[3,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[3,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_28l, y=predic_forecast28, ax=axes[3,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[3,1].plot(test_28l, test_28l, color='red', label='Correlación')
axes[3,1].set_title('Correlación entre valores reales y estimados Horizonte 28 días ', fontsize=16)
axes[3,1].set_xlabel('Valores Reales', fontsize=12)
axes[3,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[3,1].legend(fontsize=10)  # Mostrar la leyenda
axes[3,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[3,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)



plt.tight_layout()  # Ajustar el espaciado entre subgráficos
plt.show()
_images/9ce08d134caa358b3779d6cd905de24aecc0734d5631afd731215dec2be5f38c.png

En el primer gráfico del precio de cierre del Bitcoin, se nota una discrepancia notable entre las observaciones reales de prueba y las predicciones, lo que sugiere la presencia de errores considerables. Por otro lado, los gráficos de autocorrelación no muestran una correlación significativa entre los valores de la serie temporal y sus antecedentes en los intervalos de 7, 14, 21 y 28 días. En resumen, no se identifica un patrón claro o repetitivo en los datos a lo largo de estos horizontes.

En términos de predicción del precio del Bitcoin, se puede concluir que el modelo ARIMA utilizando la técnica de rolling ofrece un rendimiento superior en comparación con el otro modelo considerado (ARIMA sin utilizar rolling).

Ejercicio 5.#

Repita el análisis desarrollado en los pasos anteriores, considerando ahora el criterio de inferencia Bayesiana (BIC) y el criterio de información de Hannan–Quinn (HQIC) para encontrar el mejor modelo ARIMA y, compare los errores con aquellos obtenidos con el criterio de Akaike.

Criterio de inferencia Bayesiana (BIC)#

Construcción de modelo ARIMA considerando el criterio de inferencia Bayesiana (BIC) para predecir el precio de Bitcoin con los siguientes horizontes: 7, 14, 21 y 28.#

from statsmodels.tsa.arima.model import ARIMA
import warnings
warnings.filterwarnings("ignore") # suprimir advertencias durante el ajuste del modelo.


def arima_model_fit_BIC(data_df):
    # Realiza predicciones continuas usando un enfoque de desplazamiento
    # y almacena las predicciones en una lista
    data = data_df.copy()

    n_BTC = len(data.Close)
    train_size = n_BTC - 28  # Tamaño del conjunto de entrenamiento - 28 días

    train = data.Close[:train_size]  # Datos de entrenamiento (precios de cierre)
    dates_train = data.Date[:train_size]  # Fechas correspondientes a los datos de entrenamiento

#A través de la siguiente función que depende sólo del input `train` y retornarán los parámetros `p`, `d`, `q` y que definiendo el mejor modelo ARIMA con el menor criterio `BIC` como medida de bondad de ajuste.
#**Nota:** Consideramos el `método: mle`para el cálculo. 

    best_bic = np.inf

    best_order = None
    best_mdl = None

    pq_rng = range(3)
    d_rng  = range(2)

    for p in pq_rng:
        for d in d_rng:
            for q in pq_rng:
                try:
                    # print(i, d, j)
                    tmp_mdl = ARIMA(train, order=(p,d,q)).fit()
                    tmp_bic = tmp_mdl.bic
                    if tmp_bic < best_bic:
                        best_bic = tmp_bic
                        best_order = (p, d, q)
                        best_mdl = tmp_mdl
                except: continue

    print('Best BIC:', best_bic)
    print('Best Order:', best_order)

    print('bic: {:6.5f} | order: {}'.format(best_bic, best_order))

    model = ARIMA(train, order=best_order)
    model_fit = model.fit()   

    return model_fit, train, dates_train, best_order
  • Indexación de parámetros para la función arima_model_fit_BIC()

La siguiente función devuelve los parámetros p, d, q que definen el mejor modelo ARIMA con el menor criterio BIC (Criterio de inferencia Bayesiana) como medida de bondad de ajuste. Además, proporciona los resultados del modelo ARIMA ajustado para datos de entrenamiento, incluyendo las fechas correspondientes. También incluye los datos y fechas del modelo ARIMA de prueba, aunque estos últimos dos no son directamente visibles en la salida de la función.

model_fit_BIC,train,dates_train,best_order_BIC = arima_model_fit_BIC(BTC_df_sorted)
Best BIC: 56500.07510711281
Best Order: (1, 1, 0)
bic: 56500.07511 | order: (1, 1, 0)

Según el criterio de BIC, se determinó que el modelo ARIMA óptimo es el (1,1,0).

  • Indexación de parámetros para la función arima_rolling()

La siguiente función devuelve las prediciones, con base a los parámetros p, d, q que definen el mejor modelo ARIMA con el menor criterio de inferencia Bayesiana (BIC) de bondad de ajuste, aprovechando datos históricos para pronosticar continuamente el precio futuro del Bitcoin durante un período (horizonte) específico.

  • Horizonte de 7 días

yhat_7_BIC = arima_rolling(train_7l, test_7l, best_order_BIC)
predicted=70686.741822, expected=63778.761719
predicted=64119.269789, expected=64062.203125
predicted=64048.394887, expected=67234.171875
predicted=67080.823962, expected=69958.812500
predicted=69837.285609, expected=69987.835938
predicted=69986.546273, expected=69455.343750
predicted=69479.008540, expected=70744.953125
  • Horizonte de 14 días

yhat_14_BIC = arima_rolling(train_14l, test_14l, best_order_BIC)
predicted=68398.041476, expected=63778.761719
predicted=64008.977909, expected=64062.203125
predicted=64048.378546, expected=67234.171875
predicted=67080.638715, expected=69958.812500
predicted=69837.106590, expected=69987.835938
predicted=69986.544362, expected=69455.343750
predicted=69479.043593, expected=70744.953125
predicted=70687.201487, expected=69892.828125
predicted=69931.343935, expected=69645.304688
predicted=69656.468100, expected=71333.648438
predicted=71257.217901, expected=69702.148438
predicted=69777.748584, expected=65446.972656
predicted=65631.927891, expected=65980.812500
predicted=65957.288898, expected=68508.843750
  • Horizonte de 21 días

yhat_21_BIC  = arima_rolling(train_21l, test_21l, best_order_BIC)
predicted=70085.877875, expected=63778.761719
predicted=64071.200715, expected=64062.203125
predicted=64049.014585, expected=67234.171875
predicted=67087.740782, expected=69958.812500
predicted=69843.048328, expected=69987.835938
predicted=69986.607402, expected=69455.343750
predicted=69477.886962, expected=70744.953125
predicted=70690.006993, expected=69892.828125
predicted=69929.486737, expected=69645.304688
predicted=69655.929104, expected=71333.648438
predicted=71260.898011, expected=69702.148438
predicted=69774.173619, expected=65446.972656
predicted=65622.813590, expected=65980.812500
predicted=65958.433218, expected=68508.843750
predicted=68404.212247, expected=67837.640625
predicted=67865.796314, expected=68896.109375
predicted=68851.417954, expected=69362.554688
predicted=69342.960272, expected=71631.359375
predicted=71536.991482, expected=69139.015625
predicted=69248.028883, expected=70587.882812
predicted=70522.598743, expected=70060.609375
  • Horizonte de 28 días

yhat_28_BIC  = arima_rolling(train_28l, test_28l, best_order_BIC)
predicted=63518.352600, expected=63778.761719
predicted=63771.670242, expected=64062.203125
predicted=64048.018445, expected=67234.171875
predicted=67076.592806, expected=69958.812500
predicted=69833.506049, expected=69987.835938
predicted=69986.506057, expected=69455.343750
predicted=69479.746338, expected=70744.953125
predicted=70685.504586, expected=69892.828125
predicted=69932.459581, expected=69645.304688
predicted=69656.792411, expected=71333.648438
predicted=71255.009741, expected=69702.148438
predicted=69779.856220, expected=65446.972656
predicted=65637.585289, expected=65980.812500
predicted=65956.588574, expected=68508.843750
predicted=68395.500722, expected=67837.640625
predicted=67868.109450, expected=68896.109375
predicted=68847.771008, expected=69362.554688
predicted=69341.353800, expected=71631.359375
predicted=71529.198974, expected=69139.015625
predicted=69256.592213, expected=70587.882812
predicted=70517.633853, expected=70060.609375
predicted=70086.312225, expected=67195.867188
predicted=67333.791342, expected=63821.472656
predicted=63970.581490, expected=65738.726562
predicted=65649.527754, expected=63426.210938
predicted=63537.652801, expected=63811.863281
predicted=63793.183001, expected=61276.691406
predicted=61400.450212, expected=63637.105469

Gráfica lineplot modelo ARIMA para predecir el precio de Bitcoin con un horizonte de 7, 14, 21 y 28 días usando el criterio BIC.#

import matplotlib.pyplot as plt
import seaborn as sns


fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(25, 25)) # Crear la figura y el eje de la subtrama

#Gráfica horizonte 7 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[0], color='#CCCC00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_7, y=test_7, ax=axes[0], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=yhat_7_BIC  , ax=axes[0], label='Forecast', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[0].set_xlabel(None)
axes[0].legend(fontsize=14) # Mostrar la leyenda
axes[0].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[0].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 14 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[1], color='#CCCC00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_14, y=test_14, ax=axes[1], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=yhat_14_BIC  , ax=axes[1], label='Forecast', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[1].set_xlabel(None)
axes[1].legend(fontsize=14) # Mostrar la leyenda
axes[1].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[1].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 21 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[2], color='#CCCC00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_21, y=test_21, ax=axes[2], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=yhat_21_BIC  , ax=axes[2], label='Forecast', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[2].set_xlabel(None)
axes[2].legend(fontsize=14) # Mostrar la leyenda
axes[2].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[2].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 28 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[3], color='#CCCC00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_28, y=test_28, ax=axes[3], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=yhat_28_BIC  , ax=axes[3], label='Forecast', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[3].set_xlabel('Fecha', fontsize=12)
axes[3].legend(fontsize=14) # Mostrar la leyenda
axes[3].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[3].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

plt.show()
_images/1ce6cb19900e65cd9b8694a26040017b47bc226c69124e41b373a164fa89dee0.png

En el gráfico anterior se presentan las predicciones para horizontes de 7, 14, 21 y 28 días. La línea de color lima representa los datos utilizados para entrenar el modelo ARIMA. La línea azul muestra los datos reales u observaciones correspondientes a cada horizonte, mientras que la línea punteada indica las predicciones generadas por nuestro modelo ARIMA a partir de los datos de entrenamiento. Estas predicciones se comparan con los valores reales de prueba para evaluar la precisión del modelo.

También se aprecia un sesgo entre los datos reales de prueba y las predicciones proporcionadas por nuestro modelo ARIMA, basadas en los datos de entrenamiento.

A continuación se ajusta nuestro modelo ARIMA utilizando forecast() para los diferentes horizontes de predicción, 7, 14, 21 y 28 días, y así poder visualizar las predicciones del precio de cierre de la criptomoneda Bitcoin.

  • Horizonte de 7 días

predic_forecast7BIC = model_fit_BIC.forecast(7) 
print(predic_forecast7BIC)
3474    65614.921428
3475    65608.621639
3476    65608.942914
3477    65608.926530
3478    65608.927365
3479    65608.927323
3480    65608.927325
Name: predicted_mean, dtype: float64
  • Horizonte de 14 días

predic_forecast14BIC = model_fit_BIC.forecast(14) 
print(predic_forecast14BIC)
3474    65614.921428
3475    65608.621639
3476    65608.942914
3477    65608.926530
3478    65608.927365
3479    65608.927323
3480    65608.927325
3481    65608.927325
3482    65608.927325
3483    65608.927325
3484    65608.927325
3485    65608.927325
3486    65608.927325
3487    65608.927325
Name: predicted_mean, dtype: float64
  • Horizonte de 21 días

predic_forecast21BIC = model_fit_BIC.forecast(21) 
print(predic_forecast21BIC)
3474    65614.921428
3475    65608.621639
3476    65608.942914
3477    65608.926530
3478    65608.927365
3479    65608.927323
3480    65608.927325
3481    65608.927325
3482    65608.927325
3483    65608.927325
3484    65608.927325
3485    65608.927325
3486    65608.927325
3487    65608.927325
3488    65608.927325
3489    65608.927325
3490    65608.927325
3491    65608.927325
3492    65608.927325
3493    65608.927325
3494    65608.927325
Name: predicted_mean, dtype: float64
  • Horizonte de 28 días

predic_forecast28BIC = model_fit_BIC.forecast(28) 
print(predic_forecast28BIC)
3474    65614.921428
3475    65608.621639
3476    65608.942914
3477    65608.926530
3478    65608.927365
3479    65608.927323
3480    65608.927325
3481    65608.927325
3482    65608.927325
3483    65608.927325
3484    65608.927325
3485    65608.927325
3486    65608.927325
3487    65608.927325
3488    65608.927325
3489    65608.927325
3490    65608.927325
3491    65608.927325
3492    65608.927325
3493    65608.927325
3494    65608.927325
3495    65608.927325
3496    65608.927325
3497    65608.927325
3498    65608.927325
3499    65608.927325
3500    65608.927325
3501    65608.927325
Name: predicted_mean, dtype: float64

Gráfica lineplot modelo ARIMA para predecir el precio de Bitcoin con un horizonte de 7, 14, 21 y 28 días#

import matplotlib.pyplot as plt
import seaborn as sns


fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(25, 25)) # Crear la figura y el eje de la subtrama

#Gráfica horizonte 7 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[0], color='#CCCC00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_7, y=test_7, ax=axes[0], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=predic_forecast7BIC, ax=axes[0], label='Forecast', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[0].set_xlabel(None)
axes[0].legend(fontsize=14) # Mostrar la leyenda
axes[0].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[0].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 14 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[1], color='#CCCC00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_14, y=test_14, ax=axes[1], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=predic_forecast14BIC, ax=axes[1], label='Forecast', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[1].set_xlabel(None)
axes[1].legend(fontsize=14) # Mostrar la leyenda
axes[1].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[1].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 21 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[2], color='#CCCC00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_21, y=test_21, ax=axes[2], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=predic_forecast21BIC, ax=axes[2], label='Forecast', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[2].set_xlabel(None)
axes[2].legend(fontsize=14) # Mostrar la leyenda
axes[2].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[2].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 28 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[3], color='#CCCC00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_28, y=test_28, ax=axes[3], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=predic_forecast28BIC, ax=axes[3], label='Forecast', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[3].set_xlabel('Fecha', fontsize=12)
axes[3].legend(fontsize=14) # Mostrar la leyenda
axes[3].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[3].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

plt.show()
_images/d7c5e87de8b58531805ab8aaf679010e9de0572f32e2d63c8bbe6395d8093ba6.png

El gráfico previo representa la predicción del modelo ARIMA sin utilizar el método rolling, con un horizonte de pronóstico. El gráfico muestra la predicción para 7, 14, 21 y 28 días que le anteceden al último dato disponible en el conjunto de datos original. Además, se observan diferencias significativas entre las observaciones de prueba y las predicciones realizadas para cada uno de los horizontes.

Tablas de error para los ejercicios 2 y 3, utilizando las métricas: MAPE, MAE, RMSE, MSE, R2.#

  • Evaluación de las predicciones ejercicio 2 (usando rolling).

  • Horizonte de 7 días

frbic7 = forecast_accuracy(np.array(yhat_7_BIC), np.array(test_7l), "BIC usando Rolling 7 días")
frbic7 = frbic7.round(3)
print(frbic7)
                            MAPE       MAE      RMSE          MSE     R2
BIC usando Rolling 7 días  0.032  2139.501  3118.216  9723270.853 -0.328
  • Horizonte de 14 días

frbic14 = forecast_accuracy(np.array(yhat_14_BIC), np.array(test_14l), "BIC usando Rolling 14 días")
frbic14 = frbic14.round(3)
print(frbic14)
                             MAPE       MAE      RMSE          MSE     R2
BIC usando Rolling 14 días  0.026  1730.585  2283.684  5215211.245  0.104
  • Horizonte de 21 días

frbic21 = forecast_accuracy(np.array(yhat_21_BIC), np.array(test_21l), "BIC usando Rolling 21 días")
frbic21 = frbic21.round(3)
print(frbic21)
                             MAPE       MAE      RMSE          MSE     R2
BIC usando Rolling 21 días  0.024  1641.197  2247.055  5049255.616 -0.065
  • Horizonte de 28 días

frbic28 = forecast_accuracy(np.array(yhat_28_BIC), np.array(test_28l), "BIC usando Rolling 28 días")
frbic28 = frbic28.round(3)
print(frbic28)
                             MAPE       MAE      RMSE          MSE    R2
BIC usando Rolling 28 días  0.023  1574.952  1951.188  3807134.823  0.54

Al observar estas métricas, podemos afirmar que el modelo ARIMA con la técnica de Rolling, utilizando el criterio BIC, tiene un desempeño relativamente bueno en la predicción de series temporales. La precisión de las predicciones mejora a medida que aumenta el horizonte de tiempo, y el modelo tiene una capacidad aceptable para explicar la variabilidad en los datos.

  • Evaluación de las predicciones ejercicio 3 (Sin utilizar rolling).

  • Horizonte de 7 días

fbic7 = forecast_accuracy(np.array(predic_forecast7BIC), np.array(test_7l), "BIC sin Rolling 7 días")
fbic7 = fbic7.round(3)
print(fbic7)
                         MAPE       MAE      RMSE           MSE    R2
BIC sin Rolling 7 días  0.047  3245.578  3539.053  1.252490e+07 -0.71
  • Horizonte de 14 días

fbic14 = forecast_accuracy(np.array(predic_forecast14BIC), np.array(test_14l), "BIC sin Rolling 14 días")
fbic14 = fbic14.round(3)
print(fbic14)
                          MAPE       MAE     RMSE           MSE     R2
BIC sin Rolling 14 días  0.046  3163.644  3589.86  1.288710e+07 -1.213
  • Horizonte de 21 días

fbic21 =forecast_accuracy(np.array(predic_forecast21BIC), np.array(test_21l), "BIC sin Rolling 21 días")
fbic21 = fbic21.round(3)
print(fbic21)
                         MAPE       MAE      RMSE           MSE    R2
BIC sin Rolling 21 días  0.05  3454.462  3802.383  1.445812e+07 -2.05
  • Horizonte de 28 días

fbic28 = forecast_accuracy(np.array(predic_forecast28BIC), np.array(test_28l), "BIC sin Rolling 28 días")
fbic28 = fbic28.round(3)
print(fbic28)
                          MAPE       MAE      RMSE           MSE     R2
BIC sin Rolling 28 días  0.045  3083.276  3484.664  1.214288e+07 -0.469

los resultados indican que el modelo ARIMA sin la técnica de Rolling y utilizando el criterio BIC tiene un desempeño deficiente en la predicción de series temporales. Las métricas de evaluación muestran una discrepancia significativa entre las predicciones del modelo y los valores reales, así como una capacidad limitada para explicar la variabilidad en los datos

A continuación se presenta análisis de correlación entre variables (observación real y su predicción en el test, \( Corr(y_t, \tilde{y}_t) \).) para evaluar la relación lineal entre éstas a través de mapa de calor..

Gráfico de correlación entre la observación real y su predicción en el test, \( Corr(y_t, \tilde{y}_t) \).#

  • Gráfico de Correlación ejercicio 2 (usando rolling). .

import matplotlib.pyplot as plt
import seaborn as sns

fig, axes = plt.subplots(nrows=4, ncols=2, figsize=(25, 25))  # Crear la figura y el eje de la subtrama

# Gráfica horizonte 7 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_7, y=test_7, ax=axes[0,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=yhat_7_BIC, ax=axes[0,0], label='Predicción', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0,0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[0,0].set_xlabel(None)
axes[0,0].legend(fontsize=10) # Mostrar la leyenda
axes[0,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[0,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_7l, y=yhat_7_BIC, ax=axes[0,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[0,1].plot(test_7l, test_7l, color='red', label='Correlación')
axes[0,1].set_title('Correlación entre valores reales y estimados Horizonte 7 días', fontsize=16)
axes[0,1].set_xlabel('Valores Reales', fontsize=12)
axes[0,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[0,1].legend(fontsize=10)  # Mostrar la leyenda
axes[0,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[0,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)

# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_14, y=test_14, ax=axes[1,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=yhat_14_BIC, ax=axes[1,0], label='Predicción', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1,0].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[1,0].set_xlabel(None)
axes[1,0].legend(fontsize=10) # Mostrar la leyenda
axes[1,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[1,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_14l, y=yhat_14_BIC, ax=axes[1,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[1,1].plot(test_14l, test_14l, color='red', label='Correlación')
axes[1,1].set_title('Correlación entre valores reales y estimados Horizonte 14 días ', fontsize=16)
axes[1,1].set_xlabel('Valores Reales', fontsize=12)
axes[1,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[1,1].legend(fontsize=10)  # Mostrar la leyenda
axes[1,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[1,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_21, y=test_21, ax=axes[2,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=yhat_21_BIC, ax=axes[2,0], label='Predicción', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2,0].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[2,0].set_xlabel(None)
#axes[2,0].legend(fontsize=10) # Mostrar la leyenda
axes[2,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[2,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_21l, y=yhat_21_BIC, ax=axes[2,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[2,1].plot(test_21l, test_21l, color='red', label='Correlación')
axes[2,1].set_title('Correlación entre valores reales y estimados Horizonte 21 días ', fontsize=16)
axes[2,1].set_xlabel('Valores Reales', fontsize=12)
axes[2,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[2,1].legend(fontsize=10)  # Mostrar la leyenda
axes[2,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[2,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_28, y=test_28, ax=axes[3,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=yhat_28_BIC, ax=axes[3,0], label='Predicción', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3,0].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[3,0].set_xlabel(None)
#axes[3,0].legend(fontsize=10) # Mostrar la leyenda
axes[3,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[3,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_28l, y=yhat_28_BIC, ax=axes[3,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[3,1].plot(test_28l, test_28l, color='red', label='Correlación')
axes[3,1].set_title('Correlación entre valores reales y estimados Horizonte 28 días ', fontsize=16)
axes[3,1].set_xlabel('Valores Reales', fontsize=12)
axes[3,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[3,1].legend(fontsize=10)  # Mostrar la leyenda
axes[3,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[3,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)



plt.tight_layout()  # Ajustar el espaciado entre subgráficos
plt.show()
_images/a4a0c8df1f76f3d81b9a8670410c67338945ffa515c76eadc53cf598d8855970.png

El gráfico muestra predicciones para diferentes horizontes temporales (7, 14, 21 y 28 días) junto con observaciones reales de prueba. También se incluye un gráfico que correlaciona las predicciones con estas observaciones reales. En relación al primer gráfico del precio de cierre del Bitcoin, se aprecia que ambas líneas siguen tendencias similares y están próximas entre sí, lo que indica errores bajos. Sin embargo, se observa un sesgo en el tiempo que podría corregirse mediante mejoras en el modelo o la exploración de otras opciones que expliquen con mayor precisión la variabilidad en el tiempo. Los gráficos de autocorrelación revelan la ausencia de una correlación significativa entre los valores de la serie temporal y sus antecesores en los intervalos de 7, 14, 21 y 28 días. En resumen, no se observa un patrón claro o repetitivo en los datos a lo largo de estos horizontes.

  • Gráfico de Correlación ejercicio 3 (Sin utilizar rolling).

import matplotlib.pyplot as plt
import seaborn as sns

fig, axes = plt.subplots(nrows=4, ncols=2, figsize=(25, 25))  # Crear la figura y el eje de la subtrama

# Gráfica horizonte 7 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_7, y=test_7, ax=axes[0,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=predic_forecast7BIC, ax=axes[0,0], label='Predicción', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0,0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[0,0].set_xlabel(None)
axes[0,0].legend(fontsize=10) # Mostrar la leyenda
axes[0,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[0,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_7l, y=predic_forecast7BIC, ax=axes[0,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[0,1].plot(test_7l, test_7l, color='red', label='Correlación')
axes[0,1].set_title('Correlación entre valores reales y estimados Horizonte 7 días', fontsize=16)
axes[0,1].set_xlabel('Valores Reales', fontsize=12)
axes[0,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[0,1].legend(fontsize=10)  # Mostrar la leyenda
axes[0,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[0,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)

# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_14, y=test_14, ax=axes[1,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=predic_forecast14BIC, ax=axes[1,0], label='Predicción', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1,0].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[1,0].set_xlabel(None)
axes[1,0].legend(fontsize=10) # Mostrar la leyenda
axes[1,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[1,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_14l, y=predic_forecast14BIC, ax=axes[1,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[1,1].plot(test_14l, test_14l, color='red', label='Correlación')
axes[1,1].set_title('Correlación entre valores reales y estimados Horizonte 14 días ', fontsize=16)
axes[1,1].set_xlabel('Valores Reales', fontsize=12)
axes[1,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[1,1].legend(fontsize=10)  # Mostrar la leyenda
axes[1,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[1,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_21, y=test_21, ax=axes[2,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=predic_forecast21BIC, ax=axes[2,0], label='Predicción', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2,0].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[2,0].set_xlabel(None)
#axes[2,0].legend(fontsize=10) # Mostrar la leyenda
axes[2,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[2,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_21l, y=predic_forecast21BIC, ax=axes[2,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[2,1].plot(test_21l, test_21l, color='red', label='Correlación')
axes[2,1].set_title('Correlación entre valores reales y estimados Horizonte 21 días ', fontsize=16)
axes[2,1].set_xlabel('Valores Reales', fontsize=12)
axes[2,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[2,1].legend(fontsize=10)  # Mostrar la leyenda
axes[2,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[2,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_28, y=test_28, ax=axes[3,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=predic_forecast28BIC, ax=axes[3,0], label='Predicción', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3,0].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[3,0].set_xlabel(None)
#axes[3,0].legend(fontsize=10) # Mostrar la leyenda
axes[3,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[3,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_28l, y=predic_forecast28BIC, ax=axes[3,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[3,1].plot(test_28l, test_28l, color='red', label='Correlación')
axes[3,1].set_title('Correlación entre valores reales y estimados Horizonte 28 días ', fontsize=16)
axes[3,1].set_xlabel('Valores Reales', fontsize=12)
axes[3,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[3,1].legend(fontsize=10)  # Mostrar la leyenda
axes[3,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[3,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)



plt.tight_layout()  # Ajustar el espaciado entre subgráficos
plt.show()
_images/551f38637093f9f5eed00e409a1dd778245c788552f707aa437354d9813b4319.png

En el primer gráfico del precio de cierre del Bitcoin, se nota una discrepancia notable entre las observaciones reales de prueba y las predicciones, lo que sugiere la presencia de errores considerables. Por otro lado, los gráficos de autocorrelación no muestran una correlación significativa entre los valores de la serie temporal y sus antecedentes en los intervalos de 7, 14, 21 y 28 días. En resumen, no se identifica un patrón claro o repetitivo en los datos a lo largo de estos horizontes.

En términos de predicción del precio del Bitcoin, se puede concluir que el modelo ARIMA utilizando la técnica de rolling ofrece un rendimiento superior en comparación con el otro modelo considerado (ARIMA sin utilizar rolling).

Criterio de información de Hannan-Quinn (HQIC)#

Construcción de modelo ARIMA considerando el criterio de información de Hannan-Quinn (HQIC) para predecir el precio de Bitcoin con los siguientes horizontes: 7, 14, 21 y 28.#

from statsmodels.tsa.arima.model import ARIMA
import warnings
warnings.filterwarnings("ignore") # suprimir advertencias durante el ajuste del modelo.


def arima_model_fit_HQIC(data_df):
    # Realiza predicciones continuas usando un enfoque de desplazamiento
    # y almacena las predicciones en una lista
    data = data_df.copy()

    n_BTC = len(data.Close)
    train_size = n_BTC - 28  # Tamaño del conjunto de entrenamiento - 28 días

    train = data.Close[:train_size]  # Datos de entrenamiento (precios de cierre)
    dates_train = data.Date[:train_size]  # Fechas correspondientes a los datos de entrenamiento

#A través de la siguiente función que depende sólo del input `train` y retornarán los parámetros `p`, `d`, `q` y que definiendo el mejor modelo ARIMA con el menor criterio `BIC` como medida de bondad de ajuste.
#**Nota:** Consideramos el `método: mle`para el cálculo. 

    best_hqic = np.inf
    
    best_order = None
    best_mdl = None

    pq_rng = range(3)
    d_rng  = range(2)

    for p in pq_rng:
        for d in d_rng:
            for q in pq_rng:
                try:
                    # print(i, d, j)
                    tmp_mdl = ARIMA(train, order=(p,d,q)).fit()
                    tmp_hqic  = tmp_mdl.hqic 
                    if tmp_hqic  < best_hqic :
                        best_hqic  = tmp_hqic 
                        best_order = (p,d,q)
                        best_mdl = tmp_mdl
                except: continue

    print('Best BIC:', best_hqic)
    print('Best Order:', best_order)

    print('bic: {:6.5f} | order: {}'.format(best_hqic, best_order))

    model = ARIMA(train, order=best_order)
    model_fit = model.fit()   

    return model_fit, train, dates_train, best_order
  • Indexación de parámetros para la función arima_model_fit_HQIC()

La siguiente función devuelve los parámetros p, d, q que definen el mejor modelo ARIMA con el menor criterio HQIC (Criterio de información de Hanna-Quinn) como medida de bondad de ajuste. Además, proporciona los resultados del modelo ARIMA ajustado para datos de entrenamiento, incluyendo las fechas correspondientes. También incluye los datos y fechas del modelo ARIMA de prueba, aunque estos últimos dos no son directamente visibles en la salida de la función.

model_fit_HQIC, train, dates_train,best_order_HQIC = arima_model_fit_HQIC(BTC_df_sorted)
Best BIC: 56483.02518022764
Best Order: (2, 1, 2)
bic: 56483.02518 | order: (2, 1, 2)

Según el criterio de BIC, se determinó que el modelo ARIMA óptimo es el (2,1,2).

  • Indexación de parámetros para la función arima_rolling()

  • Horizonte de 7 días

yhat_7_HQIC = arima_rolling(train_7l, test_7l, best_order_HQIC)
predicted=70811.697824, expected=63778.761719
predicted=63702.802637, expected=64062.203125
predicted=63731.895968, expected=67234.171875
predicted=67074.831857, expected=69958.812500
predicted=69739.442330, expected=69987.835938
predicted=70049.771137, expected=69455.343750
predicted=69607.897443, expected=70744.953125
  • Horizonte de 14 días

yhat_14_HQIC = arima_rolling(train_14l, test_14l, best_order_HQIC)
predicted=68306.468761, expected=63778.761719
predicted=63775.368240, expected=64062.203125
predicted=64178.394141, expected=67234.171875
predicted=67186.536205, expected=69958.812500
predicted=69778.582631, expected=69987.835938
predicted=69967.571815, expected=69455.343750
predicted=69628.479481, expected=70744.953125
predicted=70829.428617, expected=69892.828125
predicted=69894.575368, expected=69645.304688
predicted=69553.891887, expected=71333.648438
predicted=71159.662792, expected=69702.148438
predicted=69775.632991, expected=65446.972656
predicted=65826.978295, expected=65980.812500
predicted=66078.119657, expected=68508.843750
  • Horizonte de 21 días

yhat_21_HQIC  = arima_rolling(train_21l, test_21l, best_order_HQIC)
predicted=69670.842268, expected=63778.761719
predicted=63893.959855, expected=64062.203125
predicted=64490.942495, expected=67234.171875
predicted=67380.598128, expected=69958.812500
predicted=69595.609441, expected=69987.835938
predicted=69540.166665, expected=69455.343750
predicted=69443.389089, expected=70744.953125
predicted=71112.269611, expected=69892.828125
predicted=70240.887507, expected=69645.304688
predicted=69593.885299, expected=71333.648438
predicted=70902.588673, expected=69702.148438
predicted=69447.439757, expected=65446.972656
predicted=65780.181813, expected=65980.812500
predicted=66478.532043, expected=68508.843750
predicted=68532.969471, expected=67837.640625
predicted=67392.225978, expected=68896.109375
predicted=68467.023232, expected=69362.554688
predicted=69410.072867, expected=71631.359375
predicted=71997.733389, expected=69139.015625
predicted=69491.377469, expected=70587.882812
predicted=70475.107264, expected=70060.609375
  • Horizonte de 28 días

yhat_28_HQIC  = arima_rolling(train_28l, test_28l, best_order_HQIC)
predicted=63546.231706, expected=63778.761719
predicted=63740.095431, expected=64062.203125
predicted=64080.281442, expected=67234.171875
predicted=67134.925950, expected=69958.812500
predicted=69926.332335, expected=69987.835938
predicted=70204.449870, expected=69455.343750
predicted=69738.947876, expected=70744.953125
predicted=70799.933774, expected=69892.828125
predicted=69876.296975, expected=69645.304688
predicted=69543.947359, expected=71333.648438
predicted=71136.912064, expected=69702.148438
predicted=69758.093163, expected=65446.972656
predicted=65733.890465, expected=65980.812500
predicted=65968.168204, expected=68508.843750
predicted=68227.812885, expected=67837.640625
predicted=67690.892240, expected=68896.109375
predicted=68860.655734, expected=69362.554688
predicted=69481.074711, expected=71631.359375
predicted=71714.529003, expected=69139.015625
predicted=69407.234820, expected=70587.882812
predicted=70588.746159, expected=70060.609375
predicted=69987.779760, expected=67195.867188
predicted=67226.844741, expected=63821.472656
predicted=63844.660023, expected=65738.726562
predicted=65460.133302, expected=63426.210938
predicted=63315.296428, expected=63811.863281
predicted=63722.237898, expected=61276.691406
predicted=61416.235410, expected=63637.105469

Gráfica lineplot modelo ARIMA para predecir el precio de Bitcoin con un horizonte de 7, 14, 21 y 28 días usando el criterio BIC.#

import matplotlib.pyplot as plt
import seaborn as sns


fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(25, 25)) # Crear la figura y el eje de la subtrama

#Gráfica horizonte 7 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[0], color='#00FF00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_7, y=test_7, ax=axes[0], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=yhat_7_HQIC  , ax=axes[0], label='Forecast', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[0].set_xlabel(None)
axes[0].legend(fontsize=14) # Mostrar la leyenda
axes[0].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[0].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 14 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[1], color='#00FF00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_14, y=test_14, ax=axes[1], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=yhat_14_HQIC  , ax=axes[1], label='Forecast', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[1].set_xlabel(None)
axes[1].legend(fontsize=14) # Mostrar la leyenda
axes[1].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[1].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 21 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[2], color='#00FF00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_21, y=test_21, ax=axes[2], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=yhat_21_HQIC  , ax=axes[2], label='Forecast', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[2].set_xlabel(None)
axes[2].legend(fontsize=14) # Mostrar la leyenda
axes[2].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[2].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 28 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[3], color='#00FF00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_28, y=test_28, ax=axes[3], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=yhat_28_HQIC  , ax=axes[3], label='Forecast', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[3].set_xlabel('Fecha', fontsize=12)
axes[3].legend(fontsize=14) # Mostrar la leyenda
axes[3].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[3].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

plt.show()
_images/6f313623acb1b4d40f41ffd374e20fbd8c1cbdfb6b1f9495256e1959391bdfb9.png

A continuación se ajusta nuestro modelo ARIMA al conjunto de entrenamiento utilizando forecast() para los diferentes horizontes de predicción, 7, 14, 21 y 28 días, para la visualización de las predicciones del precio de cierre de la criptomoneda Bitcoin.

  • Horizonte de 7 días

predic_forecast7HQIC = model_fit_HQIC.forecast(7) 
print(predic_forecast7HQIC)
3474    65458.494464
3475    65469.217327
3476    65506.959254
3477    65528.716848
3478    65513.547322
3479    65481.913168
3480    65469.104822
Name: predicted_mean, dtype: float64
  • Horizonte de 14 días

predic_forecast14HQIC = model_fit_HQIC.forecast(14) 
print(predic_forecast14HQIC)
3474    65458.494464
3475    65469.217327
3476    65506.959254
3477    65528.716848
3478    65513.547322
3479    65481.913168
3480    65469.104822
3481    65486.302922
3482    65511.754461
3483    65517.675011
3484    65500.216718
3485    65480.598282
3486    65479.724074
3487    65496.228503
Name: predicted_mean, dtype: float64
  • Horizonte de 21 días

predic_forecast21HQIC = model_fit_HQIC.forecast(21) 
print(predic_forecast21HQIC)
3474    65458.494464
3475    65469.217327
3476    65506.959254
3477    65528.716848
3478    65513.547322
3479    65481.913168
3480    65469.104822
3481    65486.302922
3482    65511.754461
3483    65517.675011
3484    65500.216718
3485    65480.598282
3486    65479.724074
3487    65496.228503
3488    65510.628019
3489    65508.029204
3490    65493.238830
3491    65483.304624
3492    65488.086505
3493    65500.759109
3494    65507.027237
Name: predicted_mean, dtype: float64
  • Horizonte de 28 días

predic_forecast28HQIC = model_fit_HQIC.forecast(28) 
print(predic_forecast28HQIC)
3474    65458.494464
3475    65469.217327
3476    65506.959254
3477    65528.716848
3478    65513.547322
3479    65481.913168
3480    65469.104822
3481    65486.302922
3482    65511.754461
3483    65517.675011
3484    65500.216718
3485    65480.598282
3486    65479.724074
3487    65496.228503
3488    65510.628019
3489    65508.029204
3490    65493.238830
3491    65483.304624
3492    65488.086505
3493    65500.759109
3494    65507.027237
3495    65501.076908
3496    65490.658316
3497    65487.277885
3498    65493.634124
3499    65501.852359
3500    65503.058979
3501    65496.839353
Name: predicted_mean, dtype: float64

Gráfica lineplot modelo ARIMA para predecir el precio de Bitcoin con un horizonte de 7, 14, 21 y 28 días#

import matplotlib.pyplot as plt
import seaborn as sns


fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(25, 25)) # Crear la figura y el eje de la subtrama

#Gráfica horizonte 7 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[0], color='#00FF00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_7, y=test_7, ax=axes[0], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=predic_forecast7HQIC, ax=axes[0], label='Forecast', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[0].set_xlabel(None)
axes[0].legend(fontsize=14) # Mostrar la leyenda
axes[0].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[0].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 14 días. 

sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[1], color='#00FF00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_14, y=test_14, ax=axes[1], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=predic_forecast14HQIC, ax=axes[1], label='Forecast', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[1].set_xlabel(None)
axes[1].legend(fontsize=14) # Mostrar la leyenda
axes[1].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[1].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 21 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[2], color='#00FF00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_21, y=test_21, ax=axes[2], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=predic_forecast21HQIC, ax=axes[2], label='Forecast', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[2].set_xlabel(None)
axes[2].legend(fontsize=14) # Mostrar la leyenda
axes[2].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[2].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

#Gráfica horizonte 28 días. 
sns.lineplot(x=dates_train[-100:], y=train[-100:], ax=axes[3], color='#00FF00', label='Training Data', linewidth=2) # Trama de línea para los datos de entrenamiento (últimos 100 días)
sns.lineplot(x=dates_28, y=test_28, ax=axes[3], label='Test Data', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=predic_forecast28HQIC, ax=axes[3], label='Forecast', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3].set_ylabel('Precio de Cierre BTC', fontsize=14)
axes[3].set_xlabel('Fecha', fontsize=12)
axes[3].legend(fontsize=14) # Mostrar la leyenda
axes[3].tick_params(axis='x', labelsize=13)  # Solo para el eje x (inferior)
axes[3].tick_params(axis='y', labelsize=13)  # Solo para el eje y (izquierdo)

plt.show()
_images/2e93fa1d12d119c9b4e1b90105858cbbd90ef2ce0ce38cd5b1a4df2df66fd029.png

El gráfico previo representa la predicción del modelo ARIMA sin utilizar el método rolling, con un horizonte de pronóstico. El gráfico muestra la predicción para 7, 14, 21 y 28 días que le anteceden al último dato disponible en el conjunto de datos original. Además, se observan diferencias significativas entre las observaciones de prueba y las predicciones realizadas para cada uno de los horizontes.

Tablas de error para los ejercicios 2 y 3, utilizando las métricas: MAPE, MAE, RMSE, MSE, R2.#

  • Evaluación de las predicciones ejercicio 2 (usando rolling).

  • Horizonte de 7 días

frhqic7 = forecast_accuracy(np.array(yhat_7_HQIC), np.array(test_7l), "HQIC usando Rolling 7 días")
frhqic7 = frhqic7.round(3)
print(frhqic7)
                             MAPE      MAE      RMSE           MSE     R2
HQIC usando Rolling 7 días  0.034  2251.21  3204.519  1.026894e+07 -0.402
  • Horizonte de 14 días

frhqic14 = forecast_accuracy(np.array(yhat_14_HQIC), np.array(test_14l), "HQIC usando Rolling 14 días")
frhqic14 = frhqic14.round(3)
print(frhqic14)
                              MAPE       MAE     RMSE         MSE     R2
HQIC usando Rolling 14 días  0.025  1701.208  2237.01  5004214.24  0.141
  • Horizonte de 21 días

frhqic21 = forecast_accuracy(np.array(yhat_21_HQIC), np.array(test_21l), "HQIC usando Rolling 21 días")
frhqic21 = frhqic21.round(3)
print(frhqic21)
                              MAPE       MAE      RMSE          MSE     R2
HQIC usando Rolling 21 días  0.024  1611.084  2130.011  4536947.766  0.043
  • Horizonte de 28 días

frhqic28= forecast_accuracy(np.array(yhat_28_HQIC), np.array(test_28l), "HQIC usando Rolling 28 días")
frhqic28 = frhqic28.round(3)
print(frhqic28)
                              MAPE      MAE      RMSE          MSE     R2
HQIC usando Rolling 28 días  0.023  1558.23  1926.854  3712766.082  0.551

Al observar estas métricas, podemos afirmar que el modelo ARIMA utilizando el criterio HQIC y la técnica de Rolling muestra una buena capacidad predictiva en general, con un rendimiento especialmente fuerte en el horizonte de 28 días en términos de precisión y capacidad para explicar la variabilidad en los datos.

  • Evaluación de las predicciones ejercicio 3 (Sin utilizar rolling).

  • Horizonte de 7 días

fhqic7 = forecast_accuracy(np.array(predic_forecast7HQIC), np.array(test_7l), "HQIC sin Rolling 7 días")
fhqic7 = fhqic7.round(3)
print(fhqic7)
                          MAPE       MAE      RMSE           MSE     R2
HQIC sin Rolling 7 días  0.048  3281.089  3606.585  1.300746e+07 -0.776
  • Horizonte de 14 días

fhqic14 = forecast_accuracy(np.array(predic_forecast14HQIC), np.array(test_14l), "HQIC sin Rolling 14 días")
fhqic14 = fhqic14.round(3)
print(fhqic14)
                           MAPE       MAE      RMSE           MSE     R2
HQIC sin Rolling 14 días  0.046  3219.495  3668.064  1.345470e+07 -1.311
  • Horizonte de 21 días

fhqic21 = forecast_accuracy(np.array(predic_forecast21HQIC), np.array(test_21l), "HQIC sin Rolling 21 días")
fhqic21 = fhqic21.round(3)
print(fhqic21)
                           MAPE      MAE      RMSE           MSE     R2
HQIC sin Rolling 21 días  0.051  3528.43  3891.219  1.514158e+07 -2.194
  • Horizonte de 28 días

fhqic28 = forecast_accuracy(np.array(predic_forecast28HQIC), np.array(test_28l), "HQIC sin Rolling 28 días")
fhqic28 = fhqic28.round(3)
print(fhqic28)
                           MAPE       MAE      RMSE           MSE     R2
HQIC sin Rolling 28 días  0.046  3126.999  3546.356  1.257664e+07 -0.521

Los resultados indican que el modelo ARIMA con el criterio HQIC sin la técnica de Rolling no logra capturar de manera efectiva la variabilidad en los datos para ninguno de los horizontes de tiempo considerados. Las métricas de evaluación revelan una discrepancia significativa entre las predicciones del modelo y los valores reales, así como una capacidad limitada para explicar la variabilidad en los datos.

A continuación se presenta análisis de correlación entre variables (observación real y su predicción en el test, \( Corr(y_t, \tilde{y}_t) \).) para evaluar la relación lineal entre éstas a través de mapa de calor..

Gráfico de correlación entre la observación real y su predicción en el test, \( Corr(y_t, \tilde{y}_t) \).#

  • Gráfico de Correlación ejercicio 2 (usando rolling).

import matplotlib.pyplot as plt
import seaborn as sns

fig, axes = plt.subplots(nrows=4, ncols=2, figsize=(25, 25))  # Crear la figura y el eje de la subtrama

# Gráfica horizonte 7 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_7, y=test_7, ax=axes[0,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=yhat_7_HQIC, ax=axes[0,0], label='Predicción', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0,0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[0,0].set_xlabel(None)
axes[0,0].legend(fontsize=10) # Mostrar la leyenda
axes[0,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[0,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_7l, y=yhat_7_HQIC, ax=axes[0,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[0,1].plot(test_7l, test_7l, color='red', label='Correlación')
axes[0,1].set_title('Correlación entre valores reales y estimados Horizonte 7 días', fontsize=16)
axes[0,1].set_xlabel('Valores Reales', fontsize=12)
axes[0,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[0,1].legend(fontsize=10)  # Mostrar la leyenda
axes[0,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[0,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)

# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_14, y=test_14, ax=axes[1,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=yhat_14_HQIC, ax=axes[1,0], label='Predicción', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1,0].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[1,0].set_xlabel(None)
axes[1,0].legend(fontsize=10) # Mostrar la leyenda
axes[1,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[1,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_14l, y=yhat_14_HQIC, ax=axes[1,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[1,1].plot(test_14l, test_14l, color='red', label='Correlación')
axes[1,1].set_title('Correlación entre valores reales y estimados Horizonte 14 días ', fontsize=16)
axes[1,1].set_xlabel('Valores Reales', fontsize=12)
axes[1,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[1,1].legend(fontsize=10)  # Mostrar la leyenda
axes[1,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[1,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 21 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_21, y=test_21, ax=axes[2,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=yhat_21_HQIC, ax=axes[2,0], label='Predicción', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2,0].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[2,0].set_xlabel(None)
#axes[2,0].legend(fontsize=10) # Mostrar la leyenda
axes[2,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[2,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_21l, y=yhat_21_HQIC, ax=axes[2,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[2,1].plot(test_21l, test_21l, color='red', label='Correlación')
axes[2,1].set_title('Correlación entre valores reales y estimados Horizonte 21 días ', fontsize=16)
axes[2,1].set_xlabel('Valores Reales', fontsize=12)
axes[2,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[2,1].legend(fontsize=10)  # Mostrar la leyenda
axes[2,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[2,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 28 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_28, y=test_28, ax=axes[3,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=yhat_28_HQIC, ax=axes[3,0], label='Predicción', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3,0].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[3,0].set_xlabel(None)
#axes[3,0].legend(fontsize=10) # Mostrar la leyenda
axes[3,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[3,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_28l, y=yhat_28_HQIC, ax=axes[3,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[3,1].plot(test_28l, test_28l, color='red', label='Correlación')
axes[3,1].set_title('Correlación entre valores reales y estimados Horizonte 28 días ', fontsize=16)
axes[3,1].set_xlabel('Valores Reales', fontsize=12)
axes[3,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[3,1].legend(fontsize=10)  # Mostrar la leyenda
axes[3,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[3,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)



plt.tight_layout()  # Ajustar el espaciado entre subgráficos
plt.show()
_images/9874f9b501dbc893a0b32cf9624628f58645b6a893219a8f882f386d2bf1a9a7.png

El gráfico muestra predicciones para diferentes horizontes temporales (7, 14, 21 y 28 días) junto con observaciones reales de prueba. También se incluye un gráfico que correlaciona las predicciones con estas observaciones reales. En relación al primer gráfico del precio de cierre del Bitcoin, se aprecia que ambas líneas siguen tendencias similares y están próximas entre sí, lo que indica errores bajos. Sin embargo, se observa un sesgo en el tiempo que podría corregirse mediante mejoras en el modelo o la exploración de otras opciones que expliquen con mayor precisión la variabilidad en el tiempo. Los gráficos de autocorrelación revelan la ausencia de una correlación significativa entre los valores de la serie temporal y sus antecesores en los intervalos de 7, 14, 21 y 28 días. En resumen, no se observa un patrón claro o repetitivo en los datos a lo largo de estos horizontes.

  • Gráfico de Correlación ejercicio 3 (Sin utilizar rolling).

import matplotlib.pyplot as plt
import seaborn as sns

fig, axes = plt.subplots(nrows=4, ncols=2, figsize=(25, 25))  # Crear la figura y el eje de la subtrama

# Gráfica horizonte 7 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_7, y=test_7, ax=axes[0,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_7, y=predic_forecast7HQIC, ax=axes[0,0], label='Predicción', color='#CC0000', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[0,0].set_title('Predicción de precio de Cierre BTC Horizonte 7 días', fontsize=16)
axes[0,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[0,0].set_xlabel(None)
axes[0,0].legend(fontsize=10) # Mostrar la leyenda
axes[0,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[0,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_7l, y=predic_forecast7HQIC, ax=axes[0,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[0,1].plot(test_7l, test_7l, color='red', label='Correlación')
axes[0,1].set_title('Correlación entre valores reales y estimados Horizonte 7 días', fontsize=16)
axes[0,1].set_xlabel('Valores Reales', fontsize=12)
axes[0,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[0,1].legend(fontsize=10)  # Mostrar la leyenda
axes[0,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[0,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)

# Gráfica horizonte 14 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_14, y=test_14, ax=axes[1,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_14, y=predic_forecast14HQIC, ax=axes[1,0], label='Predicción', color='#330099', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[1,0].set_title('Predicción de precio de Cierre BTC Horizonte 14 días', fontsize=16)
axes[1,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[1,0].set_xlabel(None)
axes[1,0].legend(fontsize=10) # Mostrar la leyenda
axes[1,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[1,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_14l, y=predic_forecast14HQIC, ax=axes[1,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[1,1].plot(test_14l, test_14l, color='red', label='Correlación')
axes[1,1].set_title('Correlación entre valores reales y estimados Horizonte 14 días ', fontsize=16)
axes[1,1].set_xlabel('Valores Reales', fontsize=12)
axes[1,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[1,1].legend(fontsize=10)  # Mostrar la leyenda
axes[1,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[1,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 21 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_21, y=test_21, ax=axes[2,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_21, y=predic_forecast21HQIC, ax=axes[2,0], label='Predicción', color='#E91E63', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[2,0].set_title('Predicción de precio de Cierre BTC Horizonte 21 días', fontsize=16)
axes[2,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[2,0].set_xlabel(None)
#axes[2,0].legend(fontsize=10) # Mostrar la leyenda
axes[2,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[2,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_21l, y=predic_forecast21HQIC, ax=axes[2,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[2,1].plot(test_21l, test_21l, color='red', label='Correlación')
axes[2,1].set_title('Correlación entre valores reales y estimados Horizonte 21 días ', fontsize=16)
axes[2,1].set_xlabel('Valores Reales', fontsize=12)
axes[2,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[2,1].legend(fontsize=10)  # Mostrar la leyenda
axes[2,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[2,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)


# Gráfica horizonte 28 días.

#Gráfica valores de prueba vs estimado
sns.lineplot(x=dates_28, y=test_28, ax=axes[3,0], label='Observación real', color='#008080', linewidth=2)# Trama de línea para los datos de prueba
sns.lineplot(x=dates_28, y=predic_forecast28HQIC, ax=axes[3,0], label='Predicción', color='#FF5722', linewidth=2, linestyle='--') # Trama de línea para los datos de pronóstico
axes[3,0].set_title('Predicción de precio de Cierre BTC Horizonte 28 días', fontsize=16)
axes[3,0].set_ylabel('Precio de Cierre BTC', fontsize=10)
axes[3,0].set_xlabel(None)
#axes[3,0].legend(fontsize=10) # Mostrar la leyenda
axes[3,0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axes[3,0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)
# Gráfico de dispersión para la correlación entre los valores reales y estimados
sns.scatterplot(x=test_28l, y=predic_forecast28HQIC, ax=axes[3,1], color='blue', label='Real vs Estimado')  
# Línea de referencia para una correlación perfecta
axes[3,1].plot(test_28l, test_28l, color='red', label='Correlación')
axes[3,1].set_title('Correlación entre valores reales y estimados Horizonte 28 días ', fontsize=16)
axes[3,1].set_xlabel('Valores Reales', fontsize=12)
axes[3,1].set_ylabel('Valores Estimados', fontsize=12)
#axes[3,1].legend(fontsize=10)  # Mostrar la leyenda
axes[3,1].tick_params(axis='x', labelsize=10)  # Tamaño de las etiquetas del eje x (inferior)
axes[3,1].tick_params(axis='y', labelsize=10)  # Tamaño de las etiquetas del eje y (izquierdo)



plt.tight_layout()  # Ajustar el espaciado entre subgráficos
plt.show()
_images/9ce08d134caa358b3779d6cd905de24aecc0734d5631afd731215dec2be5f38c.png

En el primer gráfico del precio de cierre del Bitcoin, se nota una discrepancia notable entre las observaciones reales de prueba y las predicciones, lo que sugiere la presencia de errores considerables. Por otro lado, los gráficos de autocorrelación no muestran una correlación significativa entre los valores de la serie temporal y sus antecedentes en los intervalos de 7, 14, 21 y 28 días. En resumen, no se identifica un patrón claro o repetitivo en los datos a lo largo de estos horizontes.

En términos de predicción del precio del Bitcoin, se puede concluir que el modelo ARIMA utilizando la técnica de rolling ofrece un rendimiento superior en comparación con el otro modelo considerado (ARIMA sin utilizar rolling).

Ejercicio 6.#

Realice tests de normalidad e independencia para los residuales obtenidos para cada predicción, en cada caso agregue las correspondientes conclusiones.

Test de Normalidad de los residuales a través de Kolmogorov-Smirnov#

  • Planteamiento de Hipótesis:

\[ H_0 : \text{Residuales se distribuyen normalmente}\]
\[ H_1 : \text{Residuales no se distribuyen normalmente}\]
  • Críterios de aceptación y/o rechazo:

Sí el P-value \(< 0.05 \), se rechaza la hipótesis nula con un a significancia \( \alpha = 0.05\), es decir que los datos no siguen una distribución normal.

Test de Independiencia de los residuales a través del Estadístico de Durbin Watson#

El estadístico de Durbin-Watson (DW) es una medida utilizada para evaluar la presencia de autocorrelación en los residuos de un modelo de regresión, éste toma valores entre 0 y 4. Un valor de DW cercano a 2 sugiere que no hay autocorrelación serial en los residuos. Esto significa que los residuos en un momento dado no están correlacionados con los residuos en momentos anteriores. Un valor de 2 implica la ausencia total de autocorrelación.Cuando el valor de DW se acerca a 0, indica autocorrelación positiva en los residuos. Esto significa que los residuos en un momento dado están correlacionados positivamente con los residuos en momentos anteriores. Por otro lado, si el valor de DW se acerca a 4, indica autocorrelación negativa en los residuos.

Análisis de residuales bajo el criterio Criterio de Información de Akaike (AIC).#

ei_AIC = model_fit.resid

# Prueba de normalidad de Kolmogorov-Smirnov
ks_result = kstest(ei_AIC, 'norm')
print("Prueba de Kolmogorov-Smirnov: ")
print("Estadístico:", ks_result.statistic)
print("Valor p:", ks_result.pvalue)
Prueba de Kolmogorov-Smirnov: 
Estadístico: 0.4846841666313614
Valor p: 0.0

Dado que el P-value \(=0.0 < 0.05\) hay evidencia significativa para rechazar la hipótesis nula con una significancia \(\alpha = 0.05\), es decir que los residuos no siguen una distribución.normal

# Prueba de Durbin-Watson
durbin_watson_statistic = sm.stats.stattools.durbin_watson(ei_AIC)
print("Estadístico de Durbin-Watson:", durbin_watson_statistic)
Estadístico de Durbin-Watson: 2.0205163971116895

Dado que el estadístico de Durbin_Watson \( = 2\), se puede afirmar que no existe una correlacion serial en los residuales.

# Gráficos QQ-plot y Autocorrelación en una sola figura
fig, axs = plt.subplots(1, 2, figsize=(15, 5))

# QQ-plot para los residuales
stats.probplot(ei_AIC, dist="norm", plot=axs[0])
axs[0].get_lines()[0].set_color('#008080')  # Establecer el color del gráfico
axs[0].get_lines()[0].set_markersize(2)     # Establecer el tamaño de los puntos
axs[0].set_title('QQ-plot para los residuales ordinarios', fontsize=16)
axs[0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axs[0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)

# Autocorrelación de los residuos
sm.graphics.tsa.plot_acf(ei_AIC, lags=40, ax=axs[1], color='#008080', vlines_kwargs={"colors": '#008080'})
axs[1].set_xlabel('Retraso')
axs[1].set_ylabel('Autocorrelación')
axs[1].set_title('Autocorrelación de los residuos', fontsize=16)
axs[1].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axs[1].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)

# Ajustar el espaciado entre los gráficos
plt.tight_layout()

# Mostrar la figura
plt.show()
_images/7573deaca36b22eb353fe36a3d7d7df3447e1be7c8af6c3774fd932c49c8db2e.png

Basándonos en el gráfico anterior, podemos concluir que, si bien los residuos no siguen estrictamente una distribución normal, sí se observa que cumplen con el supuesto de independencia. Esto se refiere a que los residuos no muestran patrones discernibles en su distribución a lo largo del tiempo, lo que sugiere que están aleatoriamente dispersos. Es importante destacar que este análisis se realiza con un horizonte de 7 días utilizando el criterio AIC.

Gráfica de residuos del modelo ARIMA AIC

Análisis de residuales bajo el criterio Criterio de inferencia Bayesiana (BIC).#

ei_BIC = model_fit_BIC.resid

# Prueba de normalidad de Kolmogorov-Smirnov
ks_result = kstest(ei_BIC, 'norm')
print("Prueba de Kolmogorov-Smirnov: ")
print("Estadístico:", ks_result.statistic)
print("Valor p:", ks_result.pvalue)
Prueba de Kolmogorov-Smirnov: 
Estadístico: 0.48216884720896
Valor p: 0.0

Dado que el P-value \(=0.0 < 0.05\) hay evidencia significativa para rechazar la hipótesis nula con una significancia \(\alpha = 0.05\), es decir que los residuos no siguen una distribución.normal

# Prueba de Durbin-Watson
durbin_watson_statistic = sm.stats.stattools.durbin_watson(ei_BIC)
print("Estadístico de Durbin-Watson:", durbin_watson_statistic)
Estadístico de Durbin-Watson: 1.9958621326334947

Dado que el estadístico de Durbin_Watson \( = 2,066\), se puede afirmar que no existe una correlacion serial en los residuales.

# Gráficos QQ-plot y Autocorrelación en una sola figura
fig, axs = plt.subplots(1, 2, figsize=(15, 5))

# QQ-plot para los residuales
stats.probplot(ei_BIC, dist="norm", plot=axs[0])
axs[0].get_lines()[0].set_color('#008080')  # Establecer el color del gráfico
axs[0].get_lines()[0].set_markersize(2)     # Establecer el tamaño de los puntos
axs[0].set_title('QQ-plot para los residuales ordinarios', fontsize=16)
axs[0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axs[0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)

# Autocorrelación de los residuos
sm.graphics.tsa.plot_acf(ei_BIC, lags=40, ax=axs[1], color='#008080', vlines_kwargs={"colors": '#008080'})
axs[1].set_xlabel('Retraso')
axs[1].set_ylabel('Autocorrelación')
axs[1].set_title('Autocorrelación de los residuos', fontsize=16)
axs[1].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axs[1].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)

# Ajustar el espaciado entre los gráficos
plt.tight_layout()

# Mostrar la figura
plt.show()
_images/cc7f4f71a2b66479b8b32f966e7614c334625573939eecd4e79eaabb0254f816.png

Basándonos en el gráfico anterior, podemos concluir que, si bien los residuos no siguen estrictamente una distribución normal, sí se observa que cumplen con el supuesto de independencia. Esto se refiere a que los residuos no muestran patrones discernibles en su distribución a lo largo del tiempo, lo que sugiere que están aleatoriamente dispersos. Es importante destacar que este análisis se realiza con un horizonte de 7 días utilizando el criterio BIC.

Análisis de residuales bajo el Criterio de información de Hannan-Quinn (HQIC).#

ei_HQIC = model_fit_HQIC.resid

# Prueba de normalidad de Kolmogorov-Smirnov
ks_result = kstest(ei_HQIC, 'norm')
print("Prueba de Kolmogorov-Smirnov: ")
print("Estadístico:", ks_result.statistic)
print("Valor p:", ks_result.pvalue)
Prueba de Kolmogorov-Smirnov: 
Estadístico: 0.4846841666313614
Valor p: 0.0

Dado que el P-value \(=0.0 < 0.05\) hay evidencia significativa para rechazar la hipótesis nula con una significancia \(\alpha = 0.05\), es decir que los residuos no siguen una distribución.normal

# Prueba de Durbin-Watson
durbin_watson_statistic = sm.stats.stattools.durbin_watson(ei_HQIC)
print("Estadístico de Durbin-Watson:", durbin_watson_statistic)
Estadístico de Durbin-Watson: 2.0205163971116895

Dado que el estadístico de Durbin_Watson \( = 2,066\), se puede afirmar que no existe una correlacion serial en los residuales.

# Gráficos QQ-plot y Autocorrelación en una sola figura
fig, axs = plt.subplots(1, 2, figsize=(15, 5))

# QQ-plot para los residuales
stats.probplot(ei_HQIC, dist="norm", plot=axs[0])
axs[0].get_lines()[0].set_color('#008080')  # Establecer el color del gráfico
axs[0].get_lines()[0].set_markersize(2)     # Establecer el tamaño de los puntos
axs[0].set_title('QQ-plot para los residuales ordinarios', fontsize=16)
axs[0].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axs[0].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)

# Autocorrelación de los residuos
sm.graphics.tsa.plot_acf(ei_HQIC, lags=40, ax=axs[1], color='#008080', vlines_kwargs={"colors": '#008080'})
axs[1].set_xlabel('Retraso')
axs[1].set_ylabel('Autocorrelación')
axs[1].set_title('Autocorrelación de los residuos', fontsize=16)
axs[1].tick_params(axis='x', labelsize=10)  # Solo para el eje x (inferior)
axs[1].tick_params(axis='y', labelsize=10)  # Solo para el eje y (izquierdo)

# Ajustar el espaciado entre los gráficos
plt.tight_layout()

# Mostrar la figura
plt.show()
_images/7573deaca36b22eb353fe36a3d7d7df3447e1be7c8af6c3774fd932c49c8db2e.png

Basándonos en el gráfico anterior, podemos concluir que, si bien los residuos no siguen estrictamente una distribución normal, sí se observa que cumplen con el supuesto de independencia. Esto se refiere a que los residuos no muestran patrones discernibles en su distribución a lo largo del tiempo, lo que sugiere que están aleatoriamente dispersos. Es importante destacar que este análisis se realiza con un horizonte de 7 días utilizando el criterio HQIC.

Comparación de modelos ARIMA basada en los criterios utilizados para su construcción#

A continuación se presenta el resultado obtenido para los tres criterios utilizados en este informe para construir nuestro mejor modelo ARIMA:

dftotal = pd.concat([fraic7, fraic14, fraic21, fraic28, faic7, faic14, faic21, faic28, frbic7, frbic14, frbic21, frbic28, fbic7, fbic14, fbic21, fbic28, frhqic7, frhqic14, frhqic21, frhqic28, fhqic7, fhqic14, fhqic21, fhqic28 ], axis=0)
dftotalr = dftotal.round(3)
print(dftotalr)
                              MAPE       MAE      RMSE           MSE     R2
AIC usando Rolling 7 días    0.021  1394.548  1797.175  3.229839e+06  0.559
AIC usando Rolling 14 días   0.022  1512.959  1967.627  3.871557e+06  0.335
AIC usando Rolling 21 días   0.021  1411.730  1810.354  3.277380e+06  0.309
AIC usando Rolling 28 días   0.024  1599.699  1957.321  3.831107e+06  0.537
AIC sin Rolling7 días        0.048  3281.089  3606.585  1.300746e+07 -0.776
AIC sin Rolling 14 días      0.046  3219.495  3668.064  1.345470e+07 -1.311
AIC sin Rolling 21 días      0.051  3528.430  3891.219  1.514158e+07 -2.194
AIC sin Rolling 28 días      0.046  3126.999  3546.356  1.257664e+07 -0.521
BIC usando Rolling 7 días    0.032  2139.501  3118.216  9.723271e+06 -0.328
BIC usando Rolling 14 días   0.026  1730.585  2283.684  5.215211e+06  0.104
BIC usando Rolling 21 días   0.024  1641.197  2247.055  5.049256e+06 -0.065
BIC usando Rolling 28 días   0.023  1574.952  1951.188  3.807135e+06  0.540
BIC sin Rolling 7 días       0.047  3245.578  3539.053  1.252490e+07 -0.710
BIC sin Rolling 14 días      0.046  3163.644  3589.860  1.288710e+07 -1.213
BIC sin Rolling 21 días      0.050  3454.462  3802.383  1.445812e+07 -2.050
BIC sin Rolling 28 días      0.045  3083.276  3484.664  1.214288e+07 -0.469
HQIC usando Rolling 7 días   0.034  2251.210  3204.519  1.026894e+07 -0.402
HQIC usando Rolling 14 días  0.025  1701.208  2237.010  5.004214e+06  0.141
HQIC usando Rolling 21 días  0.024  1611.084  2130.011  4.536948e+06  0.043
HQIC usando Rolling 28 días  0.023  1558.230  1926.854  3.712766e+06  0.551
HQIC sin Rolling 7 días      0.048  3281.089  3606.585  1.300746e+07 -0.776
HQIC sin Rolling 14 días     0.046  3219.495  3668.064  1.345470e+07 -1.311
HQIC sin Rolling 21 días     0.051  3528.430  3891.219  1.514158e+07 -2.194
HQIC sin Rolling 28 días     0.046  3126.999  3546.356  1.257664e+07 -0.521
  • AIC (Akaike Information Criterion):El modelo AIC tiende a tener MAPE, RMSE y MSE más bajos que BIC y HQIC en todos los horizontes de tiempo, lo que indica una mejor precisión en las predicciones.Además, el modelo AIC muestra valores de R2 más altos en comparación con BIC y HQIC en la mayoría de los horizontes de tiempo, indicando una mejor capacidad para explicar la variabilidad en los datos.

  • BIC (Bayesian Information Criterion):Aunque BIC muestra valores relativamente más altos en MAPE, MAE y RMSE en comparación con AIC, sigue siendo competitivo en términos de precisión de predicción en algunos horizontes de tiempo. Además, el modelo BIC muestra valores de R2 mixtos, pero en general, son menores que los del modelo AIC.

  • HQIC (Hannan-Quinn Information Criterion):El modelo HQIC muestra resultados similares a BIC en términos de precisión de predicción, con MAPE, MAE y RMSE ligeramente más altos que AIC en la mayoría de los casos. Además, Los valores de R2 de HQIC son comparables con los de BIC y en algunos casos ligeramente más bajos. En general, HQIC no parece superar consistentemente a AIC o BIC.

En términos generales, el modelo AIC parece ser la mejor opción, ya que tiende a tener la mejor precisión en la mayoría de las métricas y una capacidad razonable para explicar la variabilidad en los datos.

Conclusión:#

En términos generales, basándonos en los resultados derivados de este estudio, podemos afirmar que el modelo ARIMA que exhibe un mejor ajuste a las predicciones de nuestra serie temporal es el modelo ARIMA seleccionado a través del criterio de Información de Akaike (AIC), particularmente en las ventanas de horizonte de 7 y 28 días. Este modelo ha demostrado ser el más idóneo para capturar la complejidad inherente de la serie temporal, logrando explicar el 55.9% y el 53.9% de la variabilidad de los datos, respectivamente. En comparación con los criterios de Inferencia Bayesiana (BIC) e Información de Hannan-Quinn (HQIC), este modelo exhibe una superioridad notable al producir predicciones precisas y confiables para los datos predichos.